Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Facebook Scraper Laravel Package

piedweb/facebook-scraper

A PHP package for scraping public Facebook page content, helping retrieve posts and related data without relying on the Facebook Graph API. Suitable for lightweight Laravel/PHP projects that need basic Facebook data extraction.

View on GitHub
Deep Wiki
Context7

Getting Started

Start by confirming what the package actually returns before designing anything around it — with 3 stars and 0 dependents you are effectively the QA team.

  1. Install

    composer require piedweb/facebook-scraper
    

    There is no service provider or config to publish in most versions; it's a plain PHP class you instantiate.

  2. Look here first

    • src/ — usually a single FacebookScraper class (or similarly named). Read it top to bottom; it's small.
    • tests/ — the fixtures tell you exactly which Facebook HTML shape the parser expects and therefore how brittle it is.
    • composer.json — check whether it pulls Guzzle, Symfony DomCrawler, or symfony/http-client. That tells you where network/parse failures will surface.
    • The README's usage block — copy it verbatim for your first call, don't guess method names.
  3. First use case — pull the basics off one known-public page:

    use PiedWeb\FacebookScraper\FacebookScraper;
    
    $scraper = new FacebookScraper('https://www.facebook.com/somepublicpage');
    $data = $scraper->getData(); // check README: may be ->scrape(), ->get(), etc.
    
    // Inspect the raw shape before you type-hint anything
    dd($data);
    

    Dump first, model second. The return type (array vs. object vs. JSON string) varies between versions and there is no interface contract to lean on.

  4. Prove it end-to-end with a page you control or one you know is public and stable (a brand page, not a personal profile). If that works, you have a baseline; if it returns empty, the parse target has already drifted — see Gotchas.

Implementation Patterns

Wrap it, don't call it directly from controllers. Scraping is slow, fragile, and network-bound. Put it behind a small service:

final class FacebookPageHarvester
{
    public function harvest(string $url): ?FacebookPageData
    {
        $scraper = new FacebookScraper($url);
        $raw = $scraper->getData();

        return FacebookPageData::fromScraperArray($raw); // your DTO
    }
}

This gives you one place to patch when Facebook changes markup, and lets you swap the package out later without touching call sites — likely, given the project's size.

Queue it, always. A scrape is an outbound HTTP request plus DOM parsing. Run it in a job with $tries = 3, backoff() of several minutes, and a failed() handler that flags the record rather than throwing into the void.

class HarvestFacebookPage implements ShouldQueue
{
    public int $tries = 3;
    public array $backoff = [60, 300, 900];

    public function handle(FacebookPageHarvester $harvester): void
    {
        $data = $harvester->harvest($this->page->url);
        $this->page->update(['payload' => $data?->toArray(), 'harvested_at' => now()]);
    }
}

Cache aggressively, scrape rarely. Public page metadata changes on the order of hours, not seconds. Cache the parsed result (not the raw HTML) for 6–24 hours and serve reads from the database. This is your primary defence against rate limiting.

Cache::remember("fb:page:{$id}", now()->addHours(12), fn () => $harvester->harvest($url));

Schedule instead of on-demand. A nightly php artisan fb:harvest command iterating known pages, dispatched as individual jobs with ->delay() staggered 30–90 seconds apart, keeps you under the radar far better than burst requests triggered by user traffic.

Store raw + parsed. Persist the response body alongside your DTO. When a parse breaks, you can replay fixtures locally without re-hitting Facebook:

Storage::put("fb-raw/{$page->id}-".now()->timestamp.'.html', $rawHtml);

Fake it in tests. Never hit Facebook in CI. Bind a fake harvester that returns stored fixtures from tests/fixtures/*.html, and add at least one test asserting your DTO survives a genuinely malformed/empty response.

Integration tip: if you need images or post media, download them separately and store on your own disk/S3 — the CDN URLs Facebook returns are signed and expire.

Gotchas and Tips

The parser is the weak point, not the HTTP layer. Facebook ships obfuscated, frequently-rebundled HTML with hashed class names. Any selector in this package is a moving target. Budget for the scraper silently returning empty arrays after a Facebook deploy. Treat empty output as an error, not a valid result — assert on required fields in your DTO constructor and throw.

Consent walls and login redirects. From many IPs and regions, an unauthenticated request gets a cookie-consent interstitial or a login page with HTTP 200. Check for a known page-title or a required DOM node before trusting the body. A 200 OK means nothing here.

JavaScript-rendered content is invisible. Plain HTTP fetching cannot see anything injected client-side. If the field you want is missing from the raw HTML, the package cannot help you — you'd need a headless browser, which is out of scope. Verify what you need actually exists in the served HTML first.

Send realistic headers. A default Guzzle user agent gets blocked fast. Many versions let you pass a client or options — read the constructor. Consider a custom User-Agent, Accept-Language, and a cookie jar.

Rate limits and IP reputation. Scraping a handful of public pages nightly from one IP is usually fine; hundreds will get you 429s or a soft block. Rotate delays, respect Retry-After, and consider proxy support if you scale. There is no API quota to check — you're inferring limits from responses.

Debugging workflow when results go empty:

  1. Save the raw HTML and open it in a browser — is it the real page, a consent screen, or a login wall?
  2. Compare against the package's own test fixture; if the markup structure diverged, that's your answer.
  3. Grep the source for the exact selector/JSON key it looks for and confirm it exists in the live HTML.

Config quirks: there may be none — no config file, no env vars. Set HTTP timeouts and retries on the underlying client rather than expecting the package to expose them. Always wrap calls in try/catch for transport exceptions (connection reset, TLS, timeout) — these are distinct from "parsed nothing."

Extension points are limited. The most reliable extension is not subclassing the package but replacing a piece of it in your own service layer (custom fetcher, custom parser, custom DTO). Given the zero-dependent, three-star status, plan for eventual replacement: keep the package isolated behind one interface so a maintained fork or your own DomCrawler implementation can slot in.

Legal and ToS. Scraping Facebook public pages sits in a grey area against their Terms of Service regardless of the MIT license on the code. Confirm with whoever owns compliance that the specific pages and data you harvest are acceptable, and avoid personal data entirely.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo