我一直在尝试使用PHP和Goutte实现一个刮刀。如果我只使用一个线程并按顺序擦除所有内容,那么一切正常。为了加快我希望的过程:
当然,我正在计划一种机制,以确保一次运行的线程数量不超过一定数量。
无论如何,我的问题目前与提供Goutte客户端以及每个帖子的链接有关。
显然,Goutte客户端不是可序列化的,不能像在线程构造函数中那样传递,然后克隆,以便每个线程都有自己的Goutte客户端实例。
尝试使用原始客户端的克隆分配线程时出现以下错误:
致命错误:未捕获的异常'异常'与消息 '序列化'关闭'不允许'在 第15行的D:\ users \ Oriol \ workspace \ TravellScrapper \ pagescrapers \ baseScraper.php
这是尝试在其构造函数中克隆Goutte客户端的Thread类的代码。
class baseScrapper extends Thread{
public function __construct($client, $link){
$this->client = new Client();
$this->client = clone $client;
$this->link = $link;
threadThrottle::addThread();
}
public function run(){
$this->crowler = $this->client->click($this->link);
}
public function __destruct(){
threadThrottle::removeThread();
}
}
有关如何实现这一目标的任何建议吗?我需要在每个线程中复制Goutte客户端,以便它包含所有会话信息,并且我可以单击链接。
答案 0 :(得分:1)
使用更新版本的pthread会导致错误消失,Closures支持包含在最新版本中。
Thread-Per-Request模型并不理想,它听起来并不像线程理想;如果您只想同时下载一堆内容,那么请使用nbio。
正如其他人所提到的那样,一个按照你的建议进行扫描的僵尸程序很快就会被禁止进入任何你打算抓的网站。
答案 1 :(得分:1)
听起来使用pthreads的原因是异步执行数据通信......
Goutte基本上将Guzzle改编为Symfony\Component\BrowserKit
。在这样做的同时,它将库的异步功能抽象出来。
您可以使用GuzzleHttp\Pool制作多个concurrent requests
示例副本&从文档粘贴)
use GuzzleHttp\Pool;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
$client = new Client();
$requests = function ($total) {
$uri = 'http://127.0.0.1:8126/guzzle-server/perf';
for ($i = 0; $i < $total; $i++) {
yield new Request('GET', $uri);
}
};
$pool = new Pool($client, $requests(100), [
'concurrency' => 5,
'fulfilled' => function ($response, $index) {
// this is delivered each successful response
},
'rejected' => function ($reason, $index) {
// this is delivered each failed request
},
]);
// Initiate the transfers and create a promise
$promise = $pool->promise();
// Force the pool of requests to complete.
$promise->wait();
Goutte是一款很棒的软件;但是,听起来对于您的特定用例,异步数据通信,您可以通过单独使用底层库(Guzzle,DomCrawler甚至是\ DomDocument)来获得更好的服务。