acanto / crawler
爬取网站上的所有内部链接
Requires
- php: ^7.3|^8.0
- guzzlehttp/guzzle: ^7.2
- guzzlehttp/psr7: ^1.7
- illuminate/collections: ^8.17
- nicmart/tree: ^0.3.0
- spatie/browsershot: ^3.14
- spatie/robots-txt: ^1.0.9|^2.0
- symfony/dom-crawler: ^5.2
Requires (Dev)
- phpunit/phpunit: ^9.4
This package is not auto-updated.
Last update: 2024-09-30 05:54:00 UTC
README
此包提供了一个用于爬取网站链接的类。底层使用 Guzzle promises 来 并发爬取多个 URL。
由于爬虫可以执行 JavaScript,它可以爬取由 JavaScript 渲染的网站。底层使用 Chrome 和 Puppeteer 来实现此功能。
支持我们
我们在创建 最佳开源包 上投入了大量资源。您可以通过 购买我们的付费产品之一 来支持我们。
我们非常感谢您从您的家乡给我们寄来明信片,说明您正在使用我们的哪个包。您可以在我们的 联系方式页面 上找到我们的地址。我们将所有收到的明信片发布在我们的 虚拟明信片墙 上。
安装
此包可以通过 Composer 安装
composer require spatie/crawler
使用方法
爬虫可以按如下方式实例化
use Acanto\Crawler\Crawler;
Crawler::create()
->setCrawlObserver(<class that extends \Acanto\Crawler\CrawlObservers\CrawlObserver>)
->startCrawling($url);
传递给 setCrawlObserver
的参数必须是一个扩展了 \Acanto\Crawler\CrawlObservers\CrawlObserver
抽象类的对象
namespace Acanto\Crawler;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
abstract class CrawlObserver
{
/**
* Called when the crawler will crawl the url.
*
* @param \Psr\Http\Message\UriInterface $url
*/
public function willCrawl(UriInterface $url)
{
}
/**
* Called when the crawler has crawled the given url successfully.
*
* @param \Psr\Http\Message\UriInterface $url
* @param \Psr\Http\Message\ResponseInterface $response
* @param \Psr\Http\Message\UriInterface|null $foundOnUrl
*/
abstract public function crawled(
UriInterface $url,
ResponseInterface $response,
?UriInterface $foundOnUrl = null
);
/**
* Called when the crawler had a problem crawling the given url.
*
* @param \Psr\Http\Message\UriInterface $url
* @param \GuzzleHttp\Exception\RequestException $requestException
* @param \Psr\Http\Message\UriInterface|null $foundOnUrl
*/
abstract public function crawlFailed(
UriInterface $url,
RequestException $requestException,
?UriInterface $foundOnUrl = null
);
/**
* Called when the crawl has ended.
*/
public function finishedCrawling()
{
}
}
使用多个观察者
您可以使用 setCrawlObservers
设置多个观察者
Crawler::create()
->setCrawlObservers([
<class that extends \Acanto\Crawler\CrawlObservers\CrawlObserver>,
<class that extends \Acanto\Crawler\CrawlObservers\CrawlObserver>,
...
])
->startCrawling($url);
或者您可以使用 addCrawlObserver
逐个设置多个观察者
Crawler::create()
->addCrawlObserver(<class that extends \Acanto\Crawler\CrawlObservers\CrawlObserver>)
->addCrawlObserver(<class that extends \Acanto\Crawler\CrawlObservers\CrawlObserver>)
->addCrawlObserver(<class that extends \Acanto\Crawler\CrawlObservers\CrawlObserver>)
->startCrawling($url);
执行 JavaScript
默认情况下,爬虫不会执行 JavaScript。这是您如何启用 JavaScript 执行的方法
Crawler::create()
->executeJavaScript()
...
为了在 JavaScript 执行后获取 body html,此包依赖于我们的 Browsershot 包。此包底层使用 Puppeteer。以下是关于 如何在您的系统上安装它 的提示。
Browsershot 将根据您的系统上安装的位置,做出有根据的猜测。默认情况下,爬虫将实例化一个新的 Browsershot 实例。您可能需要使用 setBrowsershot(Browsershot $browsershot)
方法设置一个自定义创建的实例。
Crawler::create()
->setBrowsershot($browsershot)
->executeJavaScript()
...
请注意,即使您没有 Browsershot 所需的系统依赖项,爬虫仍然可以工作。这些系统依赖项仅在您调用 executeJavaScript()
时才需要。
过滤某些 URL
您可以使用 setCrawlProfile
函数告诉爬虫不要访问某些 URL。该函数期望一个扩展了 Acanto\Crawler\CrawlProfiles\CrawlProfile
的对象
/*
* Determine if the given url should be crawled.
*/
public function shouldCrawl(UriInterface $url): bool;
此包附带三个 CrawlProfiles
CrawlAllUrls
:此配置文件将爬取所有页面上所有 URL,包括指向外部网站的 URL。CrawlInternalUrls
:此配置文件将只爬取主机页面上的内部 URL。CrawlSubdomains
:此配置文件将只爬取主机页面上的内部 URL 及其子域名。
忽略 robots.txt 和 robots meta
默认情况下,爬虫会尊重robots数据。可以通过以下方式禁用这些检查
Crawler::create()
->ignoreRobots()
...
robots数据可以来自robots.txt
文件、元标签或响应头。关于规范的更多信息可以在以下链接中找到:http://www.robotstxt.org/。
解析robots数据是通过我们的包spatie/robots-txt完成的。
接受具有rel="nofollow"属性的链接
默认情况下,爬虫将拒绝所有包含属性rel="nofollow"的链接。可以通过以下方式禁用这些检查
Crawler::create()
->acceptNofollowLinks()
...
使用自定义User Agent
为了尊重robots.txt规则中的自定义User Agent,您可以指定您自己的自定义User Agent。
Crawler::create()
->setUserAgent('my-agent')
您可以在robots.txt中为'my-agent'添加特定的爬取规则组。以下示例禁止由'my-agent'标识的爬虫爬取整个站点。
// Disallow crawling for my-agent
User-agent: my-agent
Disallow: /
设置并发请求数量
为了提高爬取速度,该包默认并发爬取10个URL。如果您想更改这个数字,可以使用setConcurrency
方法。
Crawler::create()
->setConcurrency(1) // now all urls will be crawled one by one
定义爬取限制
默认情况下,爬虫会一直爬取直到它爬取了它能找到的所有页面。如果您在受限制的环境中(如无服务器环境)工作,这种行为可能会引发问题。
可以通过以下两个选项控制爬取行为
- 总爬取限制(
setTotalCrawlLimit
):此限制定义了要爬取的最大URL数量。 - 当前爬取限制(
setCurrentCrawlLimit
):这定义了当前爬取过程中处理的URL数量。
让我们通过一些示例来阐明这两种方法之间的区别。
示例1:使用总爬取限制
setTotalCrawlLimit
方法允许限制要爬取的总URL数量,无论您调用爬虫的频率如何。
$queue = <your selection/implementation of a queue>;
// Crawls 5 URLs and ends.
Crawler::create()
->setCrawlQueue($queue)
->setTotalCrawlLimit(5)
->startCrawling($url);
// Doesn't crawl further as the total limit is reached.
Crawler::create()
->setCrawlQueue($queue)
->setTotalCrawlLimit(5)
->startCrawling($url);
示例2:使用当前爬取限制
setCurrentCrawlLimit
将设置每个执行中要爬取的URL数量的限制。此段代码将在每次执行中处理5个页面,而不设置爬取页面的总限制。
$queue = <your selection/implementation of a queue>;
// Crawls 5 URLs and ends.
Crawler::create()
->setCrawlQueue($queue)
->setCurrentCrawlLimit(5)
->startCrawling($url);
// Crawls the next 5 URLs and ends.
Crawler::create()
->setCrawlQueue($queue)
->setCurrentCrawlLimit(5)
->startCrawling($url);
示例3:结合总限制和爬取限制
这两个限制可以结合起来控制爬虫。
$queue = <your selection/implementation of a queue>;
// Crawls 5 URLs and ends.
Crawler::create()
->setCrawlQueue($queue)
->setTotalCrawlLimit(10)
->setCurrentCrawlLimit(5)
->startCrawling($url);
// Crawls the next 5 URLs and ends.
Crawler::create()
->setCrawlQueue($queue)
->setTotalCrawlLimit(10)
->setCurrentCrawlLimit(5)
->startCrawling($url);
// Doesn't crawl further as the total limit is reached.
Crawler::create()
->setCrawlQueue($queue)
->setTotalCrawlLimit(10)
->setCurrentCrawlLimit(5)
->startCrawling($url);
示例4:跨请求爬取
您可以使用setCurrentCrawlLimit
来分割长时间运行的爬取。以下示例演示了一种(简化的)方法。它由一个初始请求和任意数量的后续请求组成,继续爬取。
初始请求
要开始跨不同请求进行爬取,您需要为您选择的队列驱动程序创建一个新的队列。首先将队列实例传递给爬虫。爬虫将开始填充队列,随着页面的处理和新URL的发现而填充。在爬虫完成(使用当前爬取限制)之后,序列化和存储队列引用。
// Create a queue using your queue-driver.
$queue = <your selection/implementation of a queue>;
// Crawl the first set of URLs
Crawler::create()
->setCrawlQueue($queue)
->setCurrentCrawlLimit(10)
->startCrawling($url);
// Serialize and store your queue
$serializedQueue = serialize($queue);
后续请求
对于任何后续请求,您需要反序列化原始队列并将其传递给爬虫。
// Unserialize queue
$queue = unserialize($serializedQueue);
// Crawls the next set of URLs
Crawler::create()
->setCrawlQueue($queue)
->setCurrentCrawlLimit(10)
->startCrawling($url);
// Serialize and store your queue
$serialized_queue = serialize($queue);
行为基于队列中的信息。只有当传递了相同的队列实例时,行为才会按描述工作。当传递了全新的队列时,之前爬取的限制——即使是同一网站——也不会适用。
更多详细示例可以在以下链接中找到:这里。
设置最大爬取深度
默认情况下,爬虫会一直爬取,直到它爬取了提供的URL的每个页面。如果您想限制爬虫的深度,可以使用setMaximumDepth
方法。
Crawler::create()
->setMaximumDepth(2)
设置最大响应大小
大多数HTML页面都很小。但是爬虫可能会意外地抓取大文件,例如PDF和MP3。在这种情况下,为了保持内存使用率低,爬虫将只使用小于2MB的响应。如果在流式传输响应时,它的大小超过2MB,爬虫将停止流式传输响应。将假设响应体为空。
您可以更改最大响应大小。
// let's use a 3 MB maximum.
Crawler::create()
->setMaximumResponseSize(1024 * 1024 * 3)
在请求之间添加延迟
在某些情况下,当爬取过于积极时,您可能会遇到速率限制。为了避免这种情况,您可以使用setDelayBetweenRequests()
方法在每次请求之间添加一个暂停。这个值以毫秒为单位。
Crawler::create()
->setDelayBetweenRequests(150) // After every page crawled, the crawler will wait for 150ms
限制要解析的内容类型
默认情况下,每个找到的页面都将下载(大小不超过setMaximumResponseSize()
),并解析附加链接。您可以通过设置setParseableMimeTypes()
来限制要下载和解析的内容类型,它是一个包含允许类型的数组。
Crawler::create()
->setParseableMimeTypes(['text/html', 'text/plain'])
这将防止下载具有不同MIME类型(如二进制文件、音频/视频等)的页面主体,这些页面不太可能有嵌入的链接。此功能主要节省带宽。
使用自定义爬取队列
在爬取网站时,爬虫将把要爬取的URL放入队列中。默认情况下,此队列使用内置的ArrayCrawlQueue
存储在内存中。
当网站非常大时,您可能希望将队列存储在其他地方,例如数据库。在这种情况下,您可以编写自己的爬取队列。
有效的爬取队列是任何实现了Acanto\Crawler\CrawlQueues\CrawlQueue
接口的类。您可以通过爬虫上的setCrawlQueue
方法传递您的自定义爬取队列。
Crawler::create()
->setCrawlQueue(<implementation of \Acanto\Crawler\CrawlQueues\CrawlQueue>)
这里
变更日志
有关最近更改的更多信息,请参阅变更日志。
贡献
有关详细信息,请参阅贡献指南。
测试
首先,安装Puppeteer依赖项,否则您的测试将失败。
npm install puppeteer
要运行测试,您必须在单独的终端窗口中首先启动包含的基于node的服务器。
cd tests/server
npm install
node server.js
在服务器运行后,您就可以开始测试了。
composer tests
安全性
如果您发现任何与安全相关的问题,请通过电子邮件freek@spatie.be联系,而不是使用问题跟踪器。
Postcardware
您可以使用此包,但如果它进入您的生产环境,我们非常感谢您从您的家乡给我们寄一张明信片,说明您正在使用我们哪些包。
我们的地址是:Spatie,Kruikstraat 22,2018 安特卫普,比利时。
我们将所有收到的明信片发布在我们的公司网站上。
鸣谢
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。