Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

103 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

facebook-page-info-scraper

Public Facebook page info as a Python dict โ€” fast, no login, no API key.

PyPI Python License Downloads Monthly Weekly

Requests Playwright Scrapy Selenium


โšก Quick start

pip install facebook-page-info-scraper
from facebook_page_info_scraper import scrape_page

scrape_page("https://www.facebook.com/examplepage")

That is everything โ€” both engines install and are ready to use.


๐Ÿš€ Two engines, one result

Pick whichever suits the job. Both take the same URLs, return the same fields, and run the same extraction. They differ only in how the page is requested.

๐ŸŒ HTTP๐ŸŽญ Browser
requests withrequestsChromium via Playwright
speed~1.4 s per page~3 s per page
memorynegligible~60โ€“80 MB per page
runs JavaScriptโ€”โœ…
reads network trafficโ€”โœ… captures GraphQL responses
suitslarge lists, minimal setuppages whose data never reaches the raw HTML
from facebook_page_info_scraper import scrape_pages       # ๐ŸŒ HTTP
from facebook_page_info_scraper.spider import run         # ๐ŸŽญ Browser

scrape_pages(urls, threads=16)
run(urls, threads=8)

The browser engine downloads Chromium (~150 MB) the first time it runs โ€” pip cannot fetch browser binaries, so it happens on first use. One time only.


๐Ÿ“– What it does

Give it a Facebook page URL, get back the page's public details: name, category, email, phone, website, address, likes, ratings, opening status.

๐ŸŽฏ What it solves

Facebook page data used to require driving a real browser โ€” slow, heavy, and it broke every time Facebook changed its CSS. Both engines here read Facebook's own page payload instead, so neither depends on how the page looks.


๐ŸŒ Usage โ€” HTTP

from facebook_page_info_scraper import scrape_page, scrape_pages

page  = scrape_page("https://www.facebook.com/examplepage")
pages = scrape_pages(urls, threads=16)

print(page["email"])        # hello@example.com
print(page["page_likes"])   # 12,480
python scrape.py urls.txt                  # -> pages.json
python scrape.py urls.txt --threads 24
python scrape.py urls.txt --out output.json
scrape_page(url,   want_about=True, timeout=25)
scrape_pages(urls, threads=16, want_about=True, timeout=25)

want_about=False reads only the page header โ€” faster, but no email or phone.


๐ŸŽญ Usage โ€” Browser

from facebook_page_info_scraper.spider import run

run(urls, threads=8)                    # -> pages_browser.jsonl
run(urls, threads=8, headless=False)    # watch it work
python -m facebook_page_info_scraper.spider urls.txt --threads 8
python -m facebook_page_info_scraper.spider urls.txt --headed
python -m facebook_page_info_scraper.spider https://www.facebook.com/examplepage
run(urls,
    threads=8,                  # pages rendered at once, no cap
    out="pages_browser.jsonl",
    settle_ms=2000,             # pause after load before reading
    headless=True)

This engine works in batches โ€” pass a list of URLs rather than one at a time.


๐Ÿ“ฅ Input

Any Facebook page URL. These all work with either engine:

https://www.facebook.com/examplepage
https://facebook.com/examplepage                        (no www)
https://www.facebook.com/examplepage/                   (trailing slash)
https://www.facebook.com/pages/Example-Page/100000000000000
https://www.facebook.com/profile.php?id=100000000000000
http://sv-se.facebook.com/pages/Example-Page/100000000000000   (locale subdomain)

๐Ÿ“ค Output

Identical from either engine.

{
  "page_name": "Example Store",
  "page_id": "100000000000000",
  "page_category": "Bakery",
  "email": "hello@example.com",
  "phone_number": "+33 1 23 45 67 89",
  "page_website": "https://example.com",
  "address": "1 Example Street\n12345 Sampletown",
  "page_likes": "12,480",
  "talking_about": "215",
  "were_here": "33",
  "recommend_percent": "94",
  "page_review_number": "18",
  "open_status": "Closed now",
  "price_range": "Price Range - $",
  "bio": "Example Store. 12,480 likes ...",
  "image": "https://scontent.xx.fbcdn.net/...",
  "canonical": "https://www.facebook.com/examplepage",
  "locale": "en_US",
  "ok": True
}

Fields

field type description
page_name str Page name
page_id str Numeric Facebook page ID
page_category str | None e.g. Bakery, Clothing Store
email str | list | None Contact email(s)
phone_number str | list | None Contact phone(s)
page_website str | list | None External website(s)
address str | None Full street address
page_likes str | None Like count, e.g. 12,480
page_followers str | None Follower count
talking_about str | None "talking about this" count
were_here str | None Check-in count
recommend_percent str | None e.g. 74 from "74% recommend"
page_rate str | None Star rating, where shown
page_review_number str | None Review count
open_status str | None Closed now, Always open, โ€ฆ
price_range str | None e.g. Price Range ยท $
bio str | None Page description
image str | None Profile picture URL
canonical str Canonical page URL
locale str Language Facebook served
ok bool Whether the scrape succeeded
error str Present only when ok is False

Always check ok:

page = scrape_page(url)
print(page.get("page_name")if page.get("ok") else page.get("error"))

One thing to know. email, phone_number and page_website are a string when the page lists one, and a list when it lists several.

"email": "hello@example.com"                          # one
"email": ["sales@example.com", "press@example.com"]   # several
"email": None                                         # none

def as_list(v):
    return [] if v is None else (v if isinstance(v, list) else [v])

๐Ÿฉบ Check your run

One call tells you how a batch went. Works on records from either engine.

from facebook_page_info_scraper import scrape_pages, summarise

records = scrape_pages(urls, threads=16)
summarise(records)
{
  "total": 213,          # records in
  "ok": 211,             # scraped successfully
  "failed": 2,
  "with_about": 164,     # found the About panel
  "with_email": 150,
  "with_phone": 115,
  "with_website": 161,
  "wrong_locale": 0,     # pages Facebook served in another language
  "avg_seconds": 1.44,
  "avg_kb": 533          # bytes actually read, not page size
}

Field counts are measured against ok, not total โ€” dead URLs never drag your extraction numbers down. That makes the two problems easy to tell apart:

what moved what it means
ok dropped fetching โ€” blocks, rate limits, bad URLs
ok steady, with_email dropped extraction โ€” Facebook changed something
wrong_locale above 0 those pages came back in another language

Handy as a guard in scheduled jobs:

stats = summarise(records)
if stats["with_email"] / stats["ok"] < 0.60:
    raise SystemExit("email extraction dropped โ€” check for changes")

๐Ÿ“Š Speed

Measured on the same 183-page list, same machine.

๐ŸŒ old (Selenium) ๐ŸŒ HTTP ๐ŸŽญ Browser
per page 15โ€“20 s ~1.4 s ~3 s
pages in parallel โŒ โœ… 16+ โœ… you choose
200 pages ~50 min under 1 min ~2 min
memory ~60 MB/page negligible ~60โ€“80 MB/page
needs Chrome installed โœ… โŒ downloads its own

Both engines beat the old Selenium version by a wide margin โ€” HTTP by roughly 50ร—, the browser engine by about 6ร— while still running JavaScript.


๐Ÿ”ฎ Roadmap

An AI extraction layer is planned for the tail that deterministic rules cannot reach โ€” unlabelled free-text rows in languages whose formatting conventions vary, plus self-healing selectors that repair themselves when Facebook changes shape. It will sit behind the current parsers, not in front of them: rules first, model only where rules come up empty.


โฌ†๏ธ Upgrading from 1.x

The old Selenium class still works:

pip install facebook-page-info-scraper[selenium]
from facebook_page_info_scraper import FacebookPageInfoScraper
FacebookPageInfoScraper(url).get_page_info()

Either new engine is a recommended replacement โ€” same idea, much faster.

Two output differences:

  • page_rate and page_review_number used to be swapped. They are correct now.
  • location was a country name; the new address is the full street address.

๐Ÿค Contributing

Contributions are welcome. If you find an issue or have a suggestion, please open an issue or submit a pull request on GitHub.

๐Ÿ“ Notes

  • Only public pages. Nothing behind a login.
  • Be reasonable with threads โ€” this hits Facebook from your IP.
  • Scraping Facebook is against their Terms of Service. Use accordingly.

License

MIT โ€” see LICENSE.txt.