responsilicious / laravel-intelligent-scraper
提供了一种简单爬取网页的服务,无需了解其HTML结构。
Requires
- php: >=7.1
- ext-dom: *
- fabpot/goutte: ^3.2
- illuminate/database: ^6.0
- illuminate/events: ^6.0
- psr/log: ^1.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.4
- mockery/mockery: ^1.0
- orchestra/database: ^3.8
- orchestra/testbench: ^3.8
- phpstan/phpstan: ^0.10.1
- phpunit/phpunit: ^7.0
- squizlabs/php_codesniffer: ^3
README
此包提供了一种无需了解网页HTML结构的爬取解决方案。当检测到HTML结构发生变化时,它会自动配置。这允许你在长时间内无需手动干预继续爬取。
安装
要安装,请使用composer
composer require softonic/laravel-intelligent-scraper
要发布爬取器配置,你可以使用
php artisan vendor:publish --provider="Softonic\LaravelIntelligentScraper\ScraperProvider" --tag=config
依赖项
此包依赖于 goutte,它依赖于 guzzle,因此你可以根据你的需求自定义客户端。此包的唯一要求是你必须在处理堆栈中包含 http_error
中间件。
示例
<?php use GuzzleHttp\Handler\CurlHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; use Goutte\Client as GoutteClient; use App\MyMiddleware; $client = new GoutteClient(); $stack = new HandlerStack(); $stack->setHandler(new CurlHandler()); $stack->push(MyMiddleware::getHandler(), 'my_middleware'); // Your custom middleware $stack->push(Middleware::httpErrors(), 'http_errors'); // Required middleware for the package $guzzleClient = new GuzzleClient(['handler' => $stack]); $client->setClient($guzzleClient);
默认堆栈已经包含了 http_errors 中间件,所以除非你不在使用默认堆栈,否则你不需要这样做。
配置
有两种不同的初始配置选项。此包可以通过 数据集 或 XPath 进行配置。两种方式产生相同的结果,但根据你的XPath知识,你可能更喜欢其中一种。我们建议使用 XPath配置 方法。
基于数据集的配置
第一步是知道你想要从页面中获取哪些数据,因此你必须转到该页面,并选择你想要爬取的所有文本、图像、元数据等,并对它们进行标记,这样你就可以告诉爬取器你想要什么。
以下是从微软商店的一个示例
<?php use Softonic\LaravelIntelligentScraper\Scraper\Models\ScrapedDataset; ScrapedDataset::create([ 'url' => 'https://test.c/p/my-objective', 'type' => 'Item-definition-1', 'data' => [ 'title' => 'My title', 'body' => 'This is the body content I want to get', 'images' => [ 'https://test.c/images/1.jpg', 'https://test.c/images/2.jpg', 'https://test.c/images/3.jpg', ], ], ]);
在这个示例中,我们可以看到我们想要不同字段,我们随意标记了它们。其中一些有多个值,因此我们可以从页面上爬取项目列表。
使用这个单一的数据集,我们将能够训练我们的爬取器,并能够爬取具有相同结构的任何页面。由于页面通常根据不同的变量具有不同的结构,你应该添加不同的数据集,尝试涵盖尽可能多的页面变化。爬取器 将无法 爬取未包含在数据集中的页面变化。
一旦我们完成了工作,所有准备工作就绪。只要数据集中有足够的数据来覆盖页面上的所有新修改,你就不必担心更新,爬取器将实时重新计算修改。你可以查看 如何工作 以了解更多内部信息。
在下一节中,我们将更深入地探讨如何创建新的数据集以及可用选项。
数据集创建
数据集由 url
和 data
组成。
url
部分很简单,你只需要指出你获取数据的URL。type
部分为当前数据集提供了一个项目名称。这允许你定义多个类型。variant
识别页面变体。标识符是基于获取数据使用的XPath构建的sha1哈希。data
部分是你在其中指示要获取哪些数据并分配你想要的标签的地方。数据可以是项目列表或单个项目。
以下是一个基本示例
<?php use Softonic\LaravelIntelligentScraper\Scraper\Models\ScrapedDataset; ScrapedDataset::create([ 'url' => 'https://test.c/p/my-objective', 'type' => 'Item-definition-1', 'variant' => '8ed10778a83f1266e7ffed90205f7fb61ddcdf78', 'data' => [ 'title' => 'My title', 'body' => 'This is the body content I want to get', 'images' => [ 'https://test.c/images/1.jpg', 'https://test.c/images/2.jpg', 'https://test.c/images/3.jpg', ], ], ]);
在这个数据集中,我们希望将文本我的标题
标记为标题,并且我们还有一个图像列表,希望将其标记为图像。这样,我们可以灵活地逐个选择项目或按列表选择。
有时我们希望标记一些不干净的HTML文本,因为它可能包含不可见字符,如\r\n
。为了避免处理这些,数据集允许您添加正则表达式。
以body
字段为例的示例
<?php use Softonic\LaravelIntelligentScraper\Scraper\Models\ScrapedDataset; ScrapedDataset::create([ 'url' => 'https://test.c/p/my-objective', 'type' => 'Item-definition-1', 'variant' => '8ed10778a83f1266e7ffed90205f7fb61ddcdf78', 'data' => [ 'title' => 'My title', 'body' => regexp('/^Body starts here, but it is do long that.*$/si'), 'images' => [ 'https://test.c/images/1.jpg', 'https://test.c/images/2.jpg', 'https://test.c/images/3.jpg', ], ], ]);
通过这个修改,我们将确保即使有隐藏字符也能检测到body
。
重要爬虫尝试在所有标签中查找文本,包括子标签,因此如果您定义了一个没有限制的正则表达式,例如/.*Body starts.*/
,您将在<html>
元素中找到文本,因为该文本位于<html>
的某个子元素中。因此,请谨慎定义正则表达式。
基于Xpath的配置
在收集了所有HTML中的Xpath后,您只需创建配置模型。它们看起来像
<?php use Softonic\LaravelIntelligentScraper\Scraper\Models\Configuration; Configuration::create([ 'name' => 'title', 'type' => 'Item-definition-1', 'xpaths' => '//*[@id=title]', ]); Configuration::create([ 'name' => 'category', 'type' => 'Item-definition-1', 'xpaths' => ['//*[@id=cat]', '//*[@id=long-cat]'], ]);
在定义中,您应该为要抓取的字段命名并标识其类型。xpaths字段可以包含一个字符串或字符串数组。这是因为HTML可能根据特定页面包含不同的变体,因此您可以编写一个Xpath列表,以便按顺序检查,找到第一个结果。
用法
配置爬虫后,您将能够使用scrape
助手请求特定的抓取。
<?php scrape('https://test.c/p/my-objective', 'Item-definition-1');
如果一切按预期进行,爬取将产生一个\Softonic\LaravelIntelligentScraper\Scraper\Events\Scraped
事件。因此,将侦听器附加到该事件以接收数据。
$event->scrapeRequest->url // Url scraped $event->scrapeRequest->type // Request type $event->data // Contains all the data in a [ 'fieldName' => 'value' ] format. $event->variant // Contains the page variation sha1 hash.
所有输出字段都是数组,可以包含一个或多个结果。
如果爬取失败,则会触发一个带有爬取请求信息的\Softonic\LaravelIntelligentScraper\Scraper\Events\ScrapeFailed
事件。
$event->scrapeRequest->url // Url scraped $event->scrapeRequest->type // Request type
高级用法
还有一个名为ConfigurationScraped
的事件,当在重新配置步骤中自动完成爬取时触发。它与Scraped
事件完全相同。它被命名为不同,因为它通常不感兴趣,除非在内部用于更新数据集。
可以使用ConfigurationScraped
进行更新或了解配置过程的相关信息。
队列工作者
您需要一个工作者,一个用于默认队列,另一个用于configure
队列。configure
工作者应该是一个单独的工作者,以避免并行配置。
php artisan queue:work # As many as you want php artisan queue:work --queue=configure # Just one
测试
softonic/laravel-intelligent-scraper
有一个PHPUnit测试套件和一个使用PHP CS Fixer的编码风格合规性测试套件。
要运行测试,请从项目文件夹中运行以下命令。
$ docker-compose run test
要使用PsySH进行交互式运行
$ docker-compose run psysh
它如何工作?
爬虫是自动可配置的,但需要一个初始数据集或添加配置。数据集告诉配置器您需要哪些数据以及如何标记它们。
有三个服务具有独特的责任,并通过事件系统连接。
抓取
当系统接收到\Softonic\LaravelIntelligentScraper\Scraper\Events\ScrapeRequest
事件时,它会被触发。可以使用我们的scrape($url, $type)
辅助函数来完成。
# Powered by https://code2flow.com/app
Scrape Request 'https://test.c/p/my-onjective' using 'Item-definition-1';
try {
load configuration;
}
catch(Missing config) {
goto fail;
}
extract data using configuration;
// It could be produced by old configuration
if(Error extracting data) {
goto fail
}
fire Scraped Event;
return;
fail:
fire InvalidConfiguration Event;
更新数据集
为了可重新配置并保持数据集的新鲜性,爬虫会自动存储最新抓取的数据。
# Powered by https://code2flow.com/app
Receive Scraped event;
Remove oldest scraped data;
Store scraped data;
Scrape dataset updated;
配置爬虫
如果触发无效配置事件,系统将尝试计算新的配置以从ScrapeRequest获取信息。
# Powered by https://code2flow.com/app
Invalid Configuration for ScrapeRequest;
try {
calculate configuration;
}
catch(Cannot be reconfigured) {
goto fail;
}
extract data using configuration;
if(Error extracting data) {
goto fail;
}
Store new configuerion;
Scraped data;
return;
fail:
Fire ScrapeFailed Event;
No scraped data;
此过程可能产生两个不同的事件
- Scraped:一切按预期进行,页面已被抓取
- 爬取失败:在重新计算配置后无法进行爬取,因此我们需要手动配置操作来修复它。
许可证
Apache 2.0 许可证。请参阅 LICENSE 获取更多信息。