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

一、项目简介与定位
spatie/crawler 是比利时团队 Spatie 开源的一个 PHP
爬虫包,定位是"用最简洁的 API 在一个站点上爬取链接"。底层基于 Guzzle
promises 实现并发请求,并通过 spatie/browsershot(Chrome +
Puppeteer)支持 JavaScript 渲染页面的抓取。它既可以作为独立 PHP
包使用,也能很自然地注入到 Laravel 服务容器中。
与 Scrapy 这种"全家桶框架"不同,spatie/crawler 是一个类库:你写好要做什么(发现链接、抓取响应、停止条件),它负责并发调度与去重。
二、开源协议
spatie/crawler 采用 MIT License,商业与非商业项目均可自由使用。
三、官方仓库链接
- 仓库:https://github.com/spatie/crawler
- 文档:https://spatie.be/docs/crawler
- Packagist:https://packagist.org/packages/spatie/crawler
四、前置要求
根据 Packagist 与 README:
- PHP 8.2+(较新版本要求)
- Composer 2.x
- 如需要 JS 渲染,需本机安装 Google Chrome / Chromium(browsershot 依赖)
- 任意 PHP 框架或纯 PHP 脚本均可;Laravel 用户可直接走服务容器
五、安装部署
通过 Composer 安装:
composer require spatie/crawlerLaravel 项目安装后会自动注册 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) |
八、常见问题
composer require spatie/crawler报 PHP 版本不满足 升级到 PHP 8.2+;或在 composer.json 中锁定到旧版本(如^7.0),但不建议新项目再用旧版。启用
executeJavaScript()时报 Chrome 找不到 安装 Chromium,并通过browsershot的setChromePath()指定可执行文件路径。爬着爬着被目标站限流 调小
setConcurrency、加大setDelayBetweenRequests,并设置真实 UA。Laravel 中 Observer 没被触发 确认调用了
->setCrawlObserver(...)并->start();同时确认CrawlProfile没有把目标 URL 过滤掉。只想发现链接、不想下载整页 用
->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控制并发与延时,避免对目标服务器造成压力。 - 不要绕过登录态、付费墙、验证码或其他访问控制去获取未授权内容。
- 不批量采集、存储或再分发涉及个人隐私的数据。
- 本库仅用于公开数据采集、站点健康检查、学术研究与合法的商业分析,使用者自行承担合规责任。
- 尊重原作者版权,抓取到的正文、图片等内容在二次发布时应注明来源并遵守相应许可。