在有些情况,我们需要先爬取列表页,再去爬取详情页。
这种情况我们建议的实现方法是,创建两个XCrawler实例去爬取:
- 第一个用来来爬取列表,并把详情页的链接入队列。
- 第二个出队列去爬取详情。
这样做的好处是:
- 代码可读性(可维护性)大大提升
- 更好的可测试性
示例:
这里我们给出一个示例:爬取电影天堂“2018新片精品”板块中的影片名称、URL、发布时间、以及磁力链接。(磁力链接需要到详情页爬取)
完整代码如下:
<?php
require 'vendor/autoload.php';
use XCrawler\XCrawler;
use Symfony\Component\DomCrawler\Crawler;
// 爬取dytt8影片列表
$xcrawler = new XCrawler([
'name' => 'dytt8:index',
'requests' => function() {
$url = 'http://www.dytt8.net/';
yield $url;
},
'success' => function($result, $request, $xcrawler, $res_headers) {
// 把html的编码从gbk转为utf-8
$result = iconv('GBK', 'UTF-8', $result);
$crawler = new Crawler();
$crawler->addHtmlContent($result);
$list = [];
// 通过css选择器遍历影片列表
$tr_selector = '#header > div > div.bd2 > div.bd3 > div:nth-child(2) > div:nth-child(1) > div > div:nth-child(2) > div.co_content8 tr';
$crawler->filter($tr_selector)->each(function (Crawler $node, $i) use (&$list) {
$name = dom_filter($node, 'a:nth-child(2)', 'html');
if (empty($name)) {
return;
}
$url = 'http://www.dytt8.net'.dom_filter($node, 'a:nth-child(2)', 'attr', 'href');
$data = [
'name' => $name,
'url' => $url,
'time' => dom_filter($node, '.inddline font', 'html'),
];
// 把影片url、name推送到redis队列,以便进一步爬取影片下载链接
redis()->lpush('dytt8:detail_queue', json_encode($data));
$list[] = $data;
});
var_dump($list);
}
]);
$xcrawler->run();
// 爬取dytt8影片详情
$xcrawler = new XCrawler([
'name' => 'dytt8:detail',
'concurrency' => 3,
'requests' => function() {
while ($data = redis()->rpop('dytt8:detail_queue')) {
$data = json_decode($data, true);
$request = [
'uri' => $data['url'],
'callback_data' => $data,
];
yield $request;
}
},
'success' => function($result, $request, $xcrawler) {
$result = iconv('GBK', 'UTF-8', $result);
$crawler = new Crawler();
$crawler->addHtmlContent($result);
$data = $request['callback_data'];
$crawler->filter('td[style="WORD-WRAP: break-word"] a')->each(function (Crawler $node, $i) use (&$data) {
$data['download_links'][] = $node->attr('href');
});
var_dump($data);
}
]);
$xcrawler->run();
小建议
在实际开发中,我们更加建议把不同类型页面的爬虫,分别单独创建一个文件(命令)。每一个不同类型页面的爬虫可以单独执行,也可以在另一个爬虫中被调用。
因为这样做会有更好的可测试性:你可以单独对每一个爬虫进行调试或修改。