webman爬山虎插件(PHP爬蟲(chóng)引擎)

v1.0.6
版本
2024-10-20
版本更新時(shí)間
300
安裝
14
star
簡(jiǎn)介
webman的爬山虎插件: 讓爬取工作變得更加簡(jiǎn)單高效。
更新
- 本插件的使用說(shuō)明最近一次更新時(shí)間是:
2024-10-20
,由于爬山虎迭代版新增了許多新特性和API,而且完全向下兼容,所以建議將本插件更新至最新版v1.0.6
,同時(shí)確保將爬山虎同步更新至>= v1.9.9
。 - 重大更新:爬山虎自
v1.8.7
起,開(kāi)始支持無(wú)頭瀏覽器即支持運(yùn)行JavaScript代碼及其渲染的動(dòng)態(tài)頁(yè)面。使用非常簡(jiǎn)單,無(wú)縫切換,只需要通過(guò)$context
上下文啟用或禁用無(wú)頭瀏覽器開(kāi)關(guān)即可。
安裝
composer require blogdaren/webman-phpcreeper
效果圖
使用說(shuō)明
- 首先要明確一個(gè)概念:爬山虎有三種容器分別是:生產(chǎn)器、下載器、解析器。
- 編寫(xiě)一個(gè)爬蟲(chóng)非常簡(jiǎn)單:配置搞定以后,只需要在對(duì)應(yīng)容器內(nèi)的
onXXXX
回調(diào)方法內(nèi)編寫(xiě)業(yè)務(wù)邏輯即可。 - 由于爬蟲(chóng)應(yīng)用相對(duì)WEB應(yīng)用而言比較獨(dú)立,所以app內(nèi)的爬蟲(chóng)目錄結(jié)構(gòu)建議自行獨(dú)立部署。
- 首先在自己的app項(xiàng)目下手動(dòng)創(chuàng)建有效的爬蟲(chóng)目錄,比如:
app/spider
。 - 然后在爬蟲(chóng)目錄內(nèi)
app/spider
創(chuàng)建相應(yīng)的容器句柄類Hanlder。 - 最后在對(duì)應(yīng)容器內(nèi)的
onXXXX
回調(diào)方法內(nèi)編寫(xiě)業(yè)務(wù)邏輯。 - 若啟用無(wú)頭瀏覽器開(kāi)關(guān),默認(rèn)使用無(wú)頭chrome驅(qū)動(dòng),反之使用默認(rèn)的guzzle驅(qū)動(dòng),若爬取的不是動(dòng)態(tài)頁(yè),建議禁用。
舉個(gè)例子
注意:提供的例子如果未能按照預(yù)期工作,請(qǐng)檢查修改爬取規(guī)則,因?yàn)樵凑綝OM極可能更新了。
模擬抓取未來(lái)3天內(nèi)北京的天氣預(yù)報(bào)
開(kāi)發(fā)步驟
1、創(chuàng)建爬蟲(chóng)目錄:app/spider
2、創(chuàng)建生產(chǎn)器句柄類文件:app/spider/Myproducer.php
<?php
/**
* @script Myproducer.php
* @brief 生產(chǎn)器Handler
* @author blogdaren<blogdaren@163.com>
* @create 2022-04-01
*/
namespace app\spider;
use PHPCreeper\Timer;
use PHPCreeper\Crontab;
class Myproducer extends \Webman\PHPCreeper\Producer
{
/**
* @brief 生產(chǎn)任務(wù)
*
* @return mixed
*/
public function makeTask()
{
//注意:這里說(shuō)的版本并不是webman爬山虎插件的版本,而是爬山虎的版本.
//注意:這里說(shuō)的版本并不是webman爬山虎插件的版本,而是爬山虎的版本.
//注意:這里說(shuō)的版本并不是webman爬山虎插件的版本,而是爬山虎的版本.
//在v1.6.0之前,爬山虎主要使用OOP風(fēng)格的API來(lái)創(chuàng)建任務(wù):
//$producer->newTaskMan()->setXXX()->setXXX()->createTask()
//$producer->newTaskMan()->setXXX()->setXXX()->createTask($task)
//$producer->newTaskMan()->setXXX()->setXXX()->createMultiTask()
//$producer->newTaskMan()->setXXX()->setXXX()->createMultiTask($task)
//自v1.6.0開(kāi)始,爬山虎提供了更加短小便捷的API來(lái)創(chuàng)建任務(wù), 而且參數(shù)類型更加豐富:
//注意:僅僅只是擴(kuò)展,原有的API依然可以正常使用,提倡擴(kuò)展就是為了保持向下兼容。
//1. 單任務(wù)API:$task參數(shù)類型可支持:[字符串 | 一維數(shù)組]
//2. 單任務(wù)API:$producer->createTask($task);
//3. 多任務(wù)API:$task參數(shù)類型可支持:[字符串 | 一維數(shù)組 | 二維數(shù)組]
//4. 多任務(wù)API:$producer->createMultiTask($task);
//使用字符串:不推薦使用,配置受限,需要自行處理抓取結(jié)果
//$task = "http://www.weather.com.cn/weather/101010100.shtml";
//$producer->createTask($task);
//$producer->createMultiTask($task);
//任務(wù)私有context,其上下文成員與全局context完全相同,最終會(huì)采用合并覆蓋策略
$private_task_context = array(
//是否緩存下載數(shù)據(jù)(可選項(xiàng),默認(rèn)false)
'cache_enabled' => true,
//緩存下載數(shù)據(jù)存放目錄 (可選項(xiàng),默認(rèn)位于系統(tǒng)臨時(shí)目錄下)
'cache_directory' => sys_get_temp_dir() . '/DownloadCache4PHPCreeper/',
//在特定的生命周期內(nèi)是否允許重復(fù)抓取同一個(gè)URL資源(可選項(xiàng),默認(rèn)false)
'allow_url_repeat' => true,
//要不要跟蹤完整的HTTP請(qǐng)求參數(shù),開(kāi)啟后終端會(huì)顯示完整的請(qǐng)求參數(shù) [默認(rèn)false]
'track_request_args' => true,
//要不要跟蹤完整的TASK數(shù)據(jù)包,開(kāi)啟后終端會(huì)顯示完整的任務(wù)數(shù)據(jù)包 [默認(rèn)false]
'track_task_package' => true,
//在v1.6.0之前,如果rulename留空,默認(rèn)會(huì)使用 md5($task_url)作為rulename
//自v1.6.0開(kāi)始,如果rulename留空,默認(rèn)會(huì)使用 md5($task_id) 作為rulename
//所以這個(gè)配置參數(shù)是僅僅為了保持向下兼容,但是不推薦使用,因?yàn)橛袧撛陔[患
//換句話如果使用的是v1.6.0之前舊版本,那么才有可能需要激活本參數(shù) [默認(rèn)false]
'force_use_md5url_if_rulename_empty' => false,
//強(qiáng)制使用多任務(wù)創(chuàng)建API的舊版本參數(shù)風(fēng)格,保持向下兼容,不再推薦使用 [默認(rèn)false]
'force_use_old_style_multitask_args' => false,
//設(shè)置http請(qǐng)求頭:默認(rèn)引擎會(huì)自動(dòng)偽裝成常見(jiàn)的各種隨機(jī)User-Agent
'headers' => [
//'User-Agent' => 'Mozilla/5.0 Chrome/124.0.0.0 Safari/537.36',
//'Accept' => 'text/html,*/*',
],
//cookies成員的配置格式和guzzle官方不大一樣,屏蔽了cookieJar,取值[false|array]
'cookies' => [
//'domain' => 'domain.com',
//'k1' => 'v1',
//'k2' => 'v2',
],
//無(wú)頭瀏覽器,如果是動(dòng)態(tài)頁(yè)面考慮啟用,否則應(yīng)當(dāng)禁用 [默認(rèn)使用chrome且為禁用狀態(tài)]
'headless_browser' => ['headless' => false, /*更多其他無(wú)頭參數(shù)*/],
//除了內(nèi)置參數(shù)之外,還可以自由配置自定義參數(shù),在上下游業(yè)務(wù)鏈應(yīng)用場(chǎng)景中十分有用
'user_define_arg1' => 'user_define_value1',
'user_define_arg2' => 'user_define_value2',
//更多其他上下文參數(shù)詳見(jiàn)官方手冊(cè)
);
$task = array(
'active' => true, //是否激活當(dāng)前任務(wù),只有配置為false才會(huì)凍結(jié)任務(wù),默認(rèn)true
'url' => 'http://www.weather.com.cn/weather/101010100.shtml',
"rule" => array( //如果該字段留空默認(rèn)將返回原始下載數(shù)據(jù)
'日子' => ['div#7d ul.t.clearfix h1', 'text', [], 'function($field_name, $data){
return date("Y-m-d") . " | " . $data;
}'], //關(guān)于回調(diào)字符串的用法務(wù)必詳看官方手冊(cè)
'天氣' => ['div#7d ul.t.clearfix p.wea', 'text'],
'溫度' => ['div#7d ul.t.clearfix p.tem', 'text'],
),
'rule_name' => '', //如果留空將使用md5($task_id)作為規(guī)則名
'refer' => '',
'type' => 'text', //已喪失原本的概念設(shè)定,可以自由設(shè)定類型
'method' => 'get',
'context' => $private_task_context,
);
$this->createTask($task);
}
/**
* @brief 使用無(wú)頭瀏覽器爬JavaScript渲染的取動(dòng)態(tài)頁(yè)面
*
* @return mixed
*/
public function makeDynamicTask()
{
$private_task_context = [
//是否緩存下載數(shù)據(jù)(可選項(xiàng),默認(rèn)false)
'cache_enabled' => true,
//緩存下載數(shù)據(jù)存放目錄 (可選項(xiàng),默認(rèn)位于系統(tǒng)臨時(shí)目錄下)
'cache_directory' => sys_get_temp_dir() . '/DownloadCache4PHPCreeper/',
//無(wú)頭瀏覽器,如果是動(dòng)態(tài)頁(yè)面考慮啟用,否則應(yīng)當(dāng)禁用 [默認(rèn)使用chrome且為禁用狀態(tài)]
'headless_browser' => ['headless' => true, /*更多其他無(wú)頭參數(shù)*/],
];
$dynamic_task = array(
'url' => "https://www.toutiao.com",
'rule' => array(
'今日頭條熱榜標(biāo)題' => ['div.show-monitor ol li a', 'aria-label'],
'今日頭條熱榜鏈接' => ['div.show-monitor ol li a', 'href'],
),
'context' => $private_task_context,
);
$this->createTask($dynamic_task);
}
/**
* @brief onProducerStart
*
* @param object $producer
*
* @return mixed
*/
public function onProducerStart($producer)
{
$this->makeTask();
$this->makeDynamicTask();
//使用Timer定時(shí)器創(chuàng)建任務(wù)
//Timer::add(5, [$this, "makeTask"], [], true);
//使用Crontab定時(shí)器創(chuàng)建任務(wù)
//new Crontab('*/5 * * * * *', function(){
//$this->makeTask();
//});
}
/**
* @brief onProducerStop
*
* @param object $producer
*
* @return mixed
*/
public function onProducerStop($producer)
{
}
/**
* @brief onProducerReload
*
* @param object $producer
*
* @return mixed
*/
public function onProducerReload($producer)
{
}
}
3、創(chuàng)建下載器句柄類文件:app/spider/Mydownloader.php
<?php
/**
* @script Mydownloader.php
* @brief 下載器Handler
* @author blogdaren<blogdaren@163.com>
* @create 2022-04-01
*/
namespace app\spider;
class Mydownloader extends \Webman\PHPCreeper\Downloader
{
/**
* @brief onDownloaderStart
*
* @param object $downloader
*
* @return mixed
*/
public function onDownloaderStart($downloader)
{
$downloader->setClientSocketAddress([
'ws://127.0.0.1:8888',
]);
}
/**
* @brief onDownloaderStop
*
* @param object $downloader
*
* @return mixed
*/
public function onDownloaderStop($downloader)
{
}
/**
* @brief onDownloaderReload
*
* @param object $downloader
*
* @return mixed
*/
public function onDownloaderReload($downloader)
{
}
/**
* @brief onDownloaderConnectToParser
*
* @param object $connection
*
* @return mixed
*/
public function onDownloaderConnectToParser($connection)
{
//$connection->bufferFull = true;
}
/**
* @brief onDownloaderMessage
*
* @param object $downloader
* @param string $parser_reply
*
* @return mixed
*/
public function onDownloaderMessage($downloader, $parser_reply)
{
//pprint($parser_reply, __METHOD__);
}
/**
* @brief onBeforeDownload
*
* @param object $downloader
* @param array $task
*
* @return mixed
*/
public function onBeforeDownload($downloader, $task)
{
//$downloader->httpClient->setConnectTimeout(3);
//$downloader->httpClient->setTransferTimeout(10);
//$downloader->httpClient->setHeaders(array());
//$downloader->httpClient->setProxy('http://127.0.0.1:8800');
}
/**
* @brief onStartDownload
*
* @param object $downloader
* @param array $task
*
* @return mixed
*/
public function onStartDownload($downloader, $task)
{
}
/**
* @brief onAfterDownload
*
* @param object $downloader
* @param array $download_data
* @param array $task
*
* @return mixed
*/
public function onAfterDownload($downloader, $download_data, $task)
{
//pprint($downloader->getDbo('test'), __METHOD__);
}
/**
* @brief onTaskEmpty
*
* @param object $downloader
*
* @return mixed
*/
public function onTaskEmpty($downloader)
{
//$downloader->createTask($task);
}
/**
* @brief onHeadlessBrowserOpenPage
*
* @param object $downloader
* @param object $browser
* @param object $page
* @param string $url
*
* @return mixed
*/
public function onHeadlessBrowserOpenPage($downloader, $browser, $page, $url)
{
//注意:靈活設(shè)計(jì)特定類型的返回值有助于對(duì)付各種復(fù)雜的應(yīng)用場(chǎng)景
//1. 返回false, 會(huì)觸發(fā)中斷后續(xù)的業(yè)務(wù)邏輯;
//2. 返回string,會(huì)觸發(fā)中斷后續(xù)的業(yè)務(wù)邏輯,一般多用于返回頁(yè)面的HTML;
//3. 返回array, 會(huì)繼續(xù)執(zhí)行后續(xù)的業(yè)務(wù)邏輯,一般多用于返回?zé)o頭瀏覽器選項(xiàng)參數(shù);
//4. 返回其他, 會(huì)繼續(xù)執(zhí)行后續(xù)的業(yè)務(wù)邏輯,相當(dāng)于是什么也沒(méi)有發(fā)生;
//注意:一般無(wú)需調(diào)用如下幾行代碼,因?yàn)榕郎交?nèi)部默認(rèn)會(huì)自動(dòng)調(diào)用無(wú)頭API做同樣的工作.
//$page->navigate($url)->waitForNavigation('firstMeaningfulPaint');
//$html = $page->getHtml();
//return $html;
}
}
4、創(chuàng)建解析器句柄類文件:app/spider/Myparser.php
<?php
/**
* @script Myparser.php
* @brief 解析器Handler
* @author blogdaren<blogdaren@163.com>
* @create 2022-04-01
*/
namespace app\spider;
class Myparser extends \Webman\PHPCreeper\Parser
{
/**
* @brief onParserStart
*
* @param object $parser
*
* @return mixed
*/
public function onParserStart($parser)
{
}
/**
* @brief onParserStop
*
* @param object $parser
*
* @return mixed
*/
public function onParserStop($parser)
{
}
/**
* @brief onParserReload
*
* @param object $parser
*
* @return mixed
*/
public function onParserReload($parser)
{
}
/**
* @brief onParserMessage
*
* @param object $parser
* @param object $connection
* @param string $download_data
*
* @return mixed
*/
public function onParserMessage($parser, $connection, $download_data)
{
//pprint(strlen($download_data), __METHOD__);
}
/**
* @brief onParserFindUrl
*
* @param object $parser
* @param string $sub_url
*
* @return mixed
*/
public function onParserFindUrl($parser, $sub_url)
{
//發(fā)現(xiàn)子URL后,也可以根據(jù)子URL創(chuàng)建新任務(wù)
//$parser->createTask($sub_url);
return $url;
}
/**
* @brief onParserExtractField
*
* @param object $parser
* @param string $download_data
* @param array $fields
*
* @return mixed
*/
public function onParserExtractField($parser, $download_data, $fields)
{
!empty($fields) && pprint($fields[$parser->task['rule_name']]);
}
}
5、修改插件的process配置文件設(shè)置對(duì)應(yīng)的Handler
<?php
use app\spider\Myproducer;
use app\spider\Mydownloader;
use app\spider\Myparser;
return [
'myproducer' => [
'handler' => Myproducer::class,
'listen' => '',
'count' => 1,
'constructor' => ['config' =>
include('spider/global.php')
],
],
'mydownloader' => [
'handler' => Mydownloader::class,
'listen' => '',
'count' => 1,
'constructor' => ['config' =>
include('spider/global.php')
],
],
'myparser' => [
'handler' => Myparser::class,
'listen' => 'websocket://0.0.0.0:8888',
'count' => 1,
'constructor' => ['config' =>
include('spider/global.php')
],
],
];
注意事項(xiàng)
- 爬蟲(chóng)應(yīng)用自有的配置文件要保持相對(duì)獨(dú)立;
- process配置內(nèi)的關(guān)于進(jìn)程構(gòu)造函數(shù)的配置一般不要?jiǎng)?
- 目前需要手動(dòng)設(shè)置下載器的
$downloader->setClientSocketAddress([])
; - 依賴redis服務(wù),所以務(wù)必啟動(dòng)
redis-server
; - 按照規(guī)范每一個(gè)獨(dú)立的容器實(shí)例最好對(duì)應(yīng)唯一的一個(gè)Handler;
爬山虎開(kāi)發(fā)文檔
- 爬山虎中文官方網(wǎng)站:http://www.phpcreeper.com
- 中文開(kāi)發(fā)文檔主節(jié)點(diǎn):http://www.phpcreeper.com/docs/
- 中文開(kāi)發(fā)文檔備節(jié)點(diǎn):http://www.blogdaren.com/docs/
- 爬山虎開(kāi)源項(xiàng)目地址:https://github.com/blogdaren/PHPCreeper