php在多线程爬虫这块确实很薄弱,但也是存在可行易实现的方案的。


实践框架:thinkphp5

要实现这个功能,需要安装两个包:

  • jaeger/querylist:可以实现一些爬网页常用的语法,比如xPath
  • jaeger/querylist-curl-multi:实现多线程发起网络操作的包
    querylist的优点是安装简单、无坑,在命令行和接口都可以使用。

相关文档:
http://www.querylist.cc/docs/...
http://www.querylist.cc/docs/...

实现步骤:

  • 1.安装包:

    composer require jaeger/querylist
    composer require jaeger/querylist-curl-multi
  • 2.php文件:

    use QL\QueryList;
    use QL\Ext\CurlMulti;
    
    //爬取列表
    public function spider(){
      $urlPool = [];
      $startPage = 1;  //从第几页开始爬取
      $workerNum = 10;  //并发执行的数量
      $host = 'https://xxxxxx?page=';
      $nowPage = 1;  //执行中用到的暂存计数器
      while(1){
    
          //生成要爬取的链接,每次循环打印$workerNum页数据
          for($i=1;$i<=$workerNum;$i++){
              $urlPool[] = $host.$nowPage;
              $nowPage++;
          }
    
          $ql = QueryList::use(CurlMulti::class);
          $ql->curlMulti($urlPool)
    
          // 每个任务成功完成调用此回调
          ->success(function (QueryList $ql,CurlMulti $curl,$r){
    
              //此处可以用xpath语法获取到相应的数据
              //也可以采用别的形式来获取数据,可查阅文档
              $data = $ql->find('#hits-list > div:nth-child(n) > div.header > div > a:nth-child(1)')->texts();
    
              //打印下当前获取到的链接 和 解析到的数据
              Log::write('Current url:'.$r['info']['url']);
              Log::write($data->all());
    
              //若有复杂逻辑,可以进行调用其他方法进行处理
              SpiderService::getInstance()->insertToDb($data->all());
          })
    
          // 每个任务失败回调
          ->error(function ($errorInfo,CurlMulti $curl){
              echo "Current url:{$errorInfo['info']['url']} \r\n";
              print_r($errorInfo['error']);
    
              //出错终止,跳出循环
              throw new Exception("报错结束");
          })
    
          ->start([
              // 最大并发数
              'maxThread' => $workerNum,
              // 错误重试次数
              'maxTry' => 3,
          ]);
    
          //每次执行完毕,重置链接池
          $urlPool = [];
      }
    }

oooonline
533 声望1 粉丝

一个程序员。