Loading...

文章背景图

spatie/crawler:用 PHP 与 Laravel 写一个站级爬虫

2026-11-06
0
- 字
- 分钟
|

spatie/crawler:用 PHP 与 Laravel 写一个站级爬虫

spatie/crawler 官方横幅

一、项目简介与定位

spatie/crawler 是比利时团队 Spatie 开源的一个 PHP 爬虫包,定位是"用最简洁的 API 在一个站点上爬取链接"。底层基于 Guzzle promises 实现并发请求,并通过 spatie/browsershot(Chrome + Puppeteer)支持 JavaScript 渲染页面的抓取。它既可以作为独立 PHP 包使用,也能很自然地注入到 Laravel 服务容器中。

与 Scrapy 这种"全家桶框架"不同,spatie/crawler 是一个类库:你写好要做什么(发现链接、抓取响应、停止条件),它负责并发调度与去重。

二、开源协议

spatie/crawler 采用 MIT License,商业与非商业项目均可自由使用。

三、官方仓库链接

四、前置要求

根据 Packagist 与 README:

  • PHP 8.2+(较新版本要求)
  • Composer 2.x
  • 如需要 JS 渲染,需本机安装 Google Chrome / Chromium(browsershot 依赖)
  • 任意 PHP 框架或纯 PHP 脚本均可;Laravel 用户可直接走服务容器

五、安装部署

通过 Composer 安装:

composer require spatie/crawler

Laravel 项目安装后会自动注册 ServiceProvider;如未自动发现,可在 config/app.php 中手动加入 Spatie\Crawler\CrawlerServiceProvider::class。

运行测试(仓库内):

composer test

六、最小示例代码

6.1 抓取并打印每个 URL 的状态码

官方 README 示例:

use Spatie\Crawler\Crawler;
use Spatie\Crawler\CrawlResponse;

Crawler::create('https://example.com')
    ->onCrawled(function (string $url, CrawlResponse $response) {
        echo "{$url}: {$response->status()}\n";
    })
    ->start();

6.2 收集整站 URL

$urls = Crawler::create('https://example.com')
    ->internalOnly()
    ->depth(3)
    ->foundUrls();
  • internalOnly():只爬同站链接。
  • depth(3):最大爬取深度 3 层。
  • foundUrls():以数组形式返回发现的全部 URL,不真正下载响应体。

6.3 不发真实请求的测试模式

Crawler::create('https://example.com')
    ->fake([
        'https://example.com' => '<html><a href="/about">About</a></html>',
        'https://example.com/about' => '<html>About page</html>',
    ])
    ->foundUrls();

适合在 PHPUnit 里对爬取规则做单元测试。

6.4 基于外部状态提前停止

use Spatie\Crawler\Crawler;

$shouldStop = false;

Crawler::create('https://example.com')
    ->shouldStopCallback(function (Crawler $crawler) use (&$shouldStop) {
        return $shouldStop;
    })
    ->onCrawled(function (string $url) use (&$shouldStop) {
        $shouldStop = true;
    })
    ->start();

6.5 在 Laravel 中注入使用

// routes/console.php 或 Artisan 命令
use Spatie\Crawler\Crawler;

Crawler::create('https://your-site.com')
    ->setCrawlObserver(new CheckBrokenLinksObserver)
    ->setCrawlProfile(new \Spatie\Crawler\CrawlProfiles\CrawlInternalUrls('https://your-site.com'))
    ->start();

自定义 Observer:

namespace App\Crawlers;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
use Spatie\Crawler\CrawlObservers\CrawlObserver;

class CheckBrokenLinksObserver extends CrawlObserver
{
    public function crawled(UriInterface $url, ResponseInterface $response, ?UriInterface $foundOnUrl = null): void
    {
        if ($response->getStatusCode() >= 400) {
            logger()->warning("Broken: {$url} (from {$foundOnUrl})");
        }
    }
}

七、配置项

常用链式配置方法:

方法 含义
->setConcurrency(10) 并发请求数
->setDelayBetweenRequests(1) 每次请求之间的延时(秒)
->setCrawlProfile(...) 爬取范围策略(内部 URL / 全部 URL / 自定义)
->setCrawlObserver(...) 注册抓取观察者
->acceptNofollowLinks() 是否跟随 rel="nofollow"
->ignoreRobots() 忽略 robots.txt(默认遵守)
->parseableMimeType(...) 允许解析的 MIME 类型
->executeJavaScript() 启用 JS 渲染(依赖 browsershot/Chrome)

八、常见问题

  1. composer require spatie/crawler 报 PHP 版本不满足 升级到 PHP 8.2+;或在 composer.json 中锁定到旧版本(如 ^7.0),但不建议新项目再用旧版。

  2. 启用 executeJavaScript() 时报 Chrome 找不到 安装 Chromium,并通过 browsershot 的 setChromePath() 指定可执行文件路径。

  3. 爬着爬着被目标站限流 调小 setConcurrency、加大 setDelayBetweenRequests,并设置真实 UA。

  4. Laravel 中 Observer 没被触发 确认调用了 ->setCrawlObserver(...) 并 ->start();同时确认 CrawlProfile 没有把目标 URL 过滤掉。

  5. 只想发现链接、不想下载整页 用 ->foundUrls(),它只解析 HTML 中的 <a href>,不真正下载响应体。

九、与同类工具对比

  • vs. Scrapy(Python):Scrapy 是完整框架,适合长期工程化采集;spatie/crawler 是 PHP 生态的轻量类库,与 Laravel 项目天然融合。
  • vs. Guzzle 手撸:Guzzle 只发请求,去重、深度、同站过滤都要自己写;spatie/crawler 把这些都封装成链式 API。
  • vs. Colly(Go):Colly 性能更高,但需要 Go 栈;PHP 团队在 Laravel 里做死链检测、站点地图生成时,spatie/crawler 上手最快。

十、许可

本项目基于 MIT License 发布。详情见仓库根目录 LICENSE.md。

十一、责任使用声明

  • 抓取前遵守目标站点的 robots.txt 与服务条款,本库默认即遵守 robots,不要轻易调用 ignoreRobots()。
  • 通过 setConcurrency 与 setDelayBetweenRequests 控制并发与延时,避免对目标服务器造成压力。
  • 不要绕过登录态、付费墙、验证码或其他访问控制去获取未授权内容。
  • 不批量采集、存储或再分发涉及个人隐私的数据。
  • 本库仅用于公开数据采集、站点健康检查、学术研究与合法的商业分析,使用者自行承担合规责任。
  • 尊重原作者版权,抓取到的正文、图片等内容在二次发布时应注明来源并遵守相应许可。
评论交流

文章目录