What the Facebook scraper collects
Give it a Page URL, username or numeric id (or a file of them) and get one row per Page: name, category, verified badge, followers, website, phone, e-mail, address, hours and the legal owner Meta confirmed. With --posts it adds the Page's latest posts with exact reaction, comment, share and video-view counts, as JSON or CSV.
Page profile
Input: A Page URL, username or numeric idOne row per Page with category, description, verified badge, rounded follower count, contacts, hours, rating and owner.
Page + latest posts
Input: A list of Pages in a text fileEach Page row plus up to three post rows with full text, time and exact reactions, comments, shares and video views.
Exact like count
Input: A Page that has run adsAdds likes_exact, the Page's exact like count read from Meta's Ad Library, at the cost of one more page load per Page.
No local browser
Input: A Page, plus TWOCAPTCHA_KEY in .envFetches each Page through 2Captcha's Scraper API: the full Page row and its first post.
From zero to Facebook data in 3 steps
Install
Clone the repository and install one browser engine. Python 3.9+.
$ git clone https://github.com/2scraper/facebook-pages-scraper.git && cd facebook-pages-scraper $ python3 -m venv .venv && source .venv/bin/activate $ pip install -r requirements-playwright.txt $ playwright install chromium $ python3 playwright_scraper.py --url nike --posts 3
Run a scraper
Pick what you need and run it. Every flag is documented in the README.
$ python3 playwright_scraper.py --url nike$ python3 playwright_scraper.py --urls-file pages.txt --posts 3 --format csv$ python3 playwright_scraper.py --url nike --posts 1 --exact-likes$ python3 playwright_scraper.py --scraper-api --url derspiegel --posts 1Get structured data
Rows land in JSON or CSV (
--format), with a.meta.jsonsidecar recording status, pages fetched and why the run stopped.
Facebook data fields
41 columns per row in a predictable schema. The most useful ones:
| Field | What it holds |
|---|---|
title | Page name, or a post's first line |
page_id | The Page id — the same id Meta's Ad Library uses |
username | The Page's address name |
page_category | Category as the Page shows it |
is_verified | Whether the Page has the blue badge |
followers | Follower count, as rounded by Facebook |
likes_exact | Exact like count from the Ad Library (with --exact-likes) |
website | Website from the intro card, unwrapped from Facebook's redirect |
phone | Phone number from the intro card |
email | E-mail from the intro card |
address | Street address from the intro card |
owner | The legal owner Meta confirmed for the Page |
posted_at | Post time in UTC |
reactions | Exact reaction count of a post |
comments | Exact comment count of a post |
video_views | Exact video-view count of a post |
[
{
"sku": "facebook-page-15087023444",
"source": "facebook.com",
"category": "page",
"title": "Nike",
"brand": null,
"price": null,
"currency": null,
"price_source": null,
"product_url": "https://www.facebook.com/nike",
"image_url": "https://scontent.fakx3-1.fna.fbcdn.net/v/t39.30808-1/284964043_10159903868513445_769635…",
"scraped_at": "2026-10-06T12:44:31Z",
"page_id": "15087023444",
"profile_id": "100044541544829",
"username": "nike",
"page_category": "Sportswear Store",
"description": "Just Do It.",
"is_verified": true,
"followers": 39000000,
"followers_text": "39M followers",
"following": 24,
"likes": null,
"likes_exact": 39506247,
"website": "http://nike.com/",
"phone": "+972 54-292-7777",
"email": null,
"address": null,
"hours": null,
"price_range": null,
"recommend_pct": null,
"review_count": null,
"owner": "NIKE, Inc.",
"other_accounts_json": null,
"cover_url": "https://scontent.fakx3-1.fna.fbcdn.net/v/t39.30808-6/285211224_10159903868008445_547733…",
"post_id": null,
"posted_at": null,
"text": null,
"reactions": null,
"comments": null,
"shares": null,
"video_views": null
},
{
"sku": "facebook-post-1393461115481927",
"source": "facebook.com",
"category": "post",
"title": "Leave your limits at the surface. #JustDoIt",
"brand": null,
"price": null,
"currency": null,
"price_source": null,
"product_url": "https://www.facebook.com/reel/2166091230582141/",
"image_url": null,
"scraped_at": "2026-10-06T12:44:31Z",
"page_id": "15087023444",
"profile_id": null,
"username": "nike",
"page_category": null,
"description": null,
"is_verified": null,
"followers": null,
"followers_text": null,
"following": null,
"likes": null,
"likes_exact": null,
"website": null,
"phone": null,
"email": null,
"address": null,
"hours": null,
"price_range": null,
"recommend_pct": null,
"review_count": null,
"owner": null,
"other_accounts_json": null,
"cover_url": null,
"post_id": "1393461115481927",
"posted_at": "2025-09-15T16:01:59Z",
"text": "Leave your limits at the surface. #JustDoIt",
"reactions": 8200,
"comments": 2644,
"shares": 1157,
"video_views": 496072
}
]First 2 of 4 rows from a real run — sample_output.json. Long values shortened here.
Handles what breaks naive Facebook scrapers
Intro-card items read by type
Each intro-card entry carries its own type (website, phone, address…), so a phone number is never mistaken for a company name and the website is taken out of Facebook's l.php redirect.
Exact engagement, not rounded labels
Post reactions, comments, shares and views are the exact numbers Facebook sends, checked against the displayed count on all 67 captured posts. For likes, --exact-likes reads the exact figure from the Ad Library.
Missing Pages are not blocks
A misspelt or removed Page answers HTTP 200 with "This content isn't available"; it is listed as not found, never retried and never reported as a block.
Private persons skipped
An address that leads to a personal profile is reported as not_a_page and nothing about it is written. Nothing about people who comment or react is collected — only the counts.
Scraping infrastructure included
The same tested core runs every 2scraper repository, so the hard parts work the same way on every site.
CAPTCHA handling
Detects the challenge a page really serves and, with a 2Captcha key, solves it only when it blocks the data — a capped number of solves per page.
Proxy rotation
Single proxy or a pool from a file, rotated on blocks with a fresh browser per exit. Credentials stay out of argv and logs.
Fingerprints
Optional complete device identity from the 2Captcha Fingerprint API — user agent, client hints, locale and timezone that agree with each other.
Scraping Browser API
Point --cdp-endpoint at a managed Chromium with its own exit country — no browser infrastructure to run yourself.
Honest run metadata
Every run writes a .meta.json sidecar and distinct exit codes: complete, blocked, empty and partial are never confused, and a failed run never overwrites good data.
Diff two runs
diff_runs.py compares two complete Facebook snapshots by id — new, removed and changed rows for monitoring.
What teams do with Facebook data
Lead generation
Turn a list of business Pages into websites, phone numbers, e-mails, addresses and opening hours.
Brand and competitor tracking
Follow follower counts and the engagement on each brand's latest posts, and diff runs to see what changed.
Contact-data hygiene
Re-check a Page list on a schedule; diff_runs.py --fail-on-change flags changed websites or phone numbers for cron or CI.
Ad-research joins
page_id is the same id Meta's Ad Library uses, so Page rows join directly with the companion facebook-ads-scraper.
Free to run. Pay only for what the site requires.
Runs with no key, proxy or account
No account, cookies, key or proxy: measured live on 2026-10-06 from an ordinary residential IP, 10 Pages and their 30 posts in 50 seconds. A datacentre address has not been measured yet. Residential proxies help for long lists or a specific exit country, the 2Captcha Scraper API runs it with no browser on your machine, and the Scraping Browser API can be attached over CDP.
Optional 2Captcha services — captcha solving, the Scraping Browser API, residential proxies and fingerprints — work behind one key.
Good to know
- Three posts per Page; no comments, reviews, photos tab or events.
- Follower counts are rounded by Facebook; no exact follower count is available.
- Groups, events and personal profiles are out of scope.
2scraper vs. hosted scraper APIs
| 2scraper | Typical hosted API | |
|---|---|---|
| Code | Open source, MIT — read and patch it | Closed |
| Cost | Free; optional services billed only when used | Subscription or per request / per result |
| Runs on | Your machine, server or CI | The vendor's infrastructure |
| Output | JSON/CSV files + run metadata | API responses |
| When the site changes | Fix it yourself or open an issue / PR | Wait for the vendor |
Facebook scraper FAQ
Do I need to log in to Facebook?
No. It reads what Facebook shows any logged-out visitor — the data a Page embeds in its own page.
How many posts can it get per Page?
Up to three (--posts 0-3). That is all a logged-out visitor gets; scrolling brought no more.
Why are follower counts round numbers?
Facebook shows logged-out visitors a rounded figure such as "39M followers"; followers_text keeps the original. --exact-likes gets the exact like count for Pages that have run ads.
Is there a captcha?
None in the live runs of 2026-10-05 and 2026-10-06 across all engines and the Scraper API. Detection stays on, so a real one would be reported as blocked rather than read as data.
What output does it produce?
JSON or CSV, one row per Page and per post, plus a .meta.json sidecar with the status and per-address results. A run that finds nothing never overwrites your previous file.
Is this allowed?
It reads only public Pages, logged out, and skips personal profiles. Facebook's robots.txt and Meta's terms restrict automated collection, so check your jurisdiction and purpose; follow the site's terms and the law.