PHP多进程抓取百度搜索结果

  1. <?php  
  2. /** 
  3.  *  多进程抓取百度结果页自然结果,包括标题、摘要、图片、链接、来源 
  4.  *  @since 2016-04-15 
  5.  */  
  6. class NaturalResultSpider {  
  7.   
  8.     private $_strQuery = null;  
  9.   
  10.     public $worker_process = 4;      //开启进程数  
  11.   
  12.     private $_arrPids = array();  
  13.   
  14.     private $_intPageNum;         //需要抓取的自然结果页数  
  15.   
  16.     public $arrAllResult = array();  
  17.   
  18.     public $dataHandler = null;    //钩子,可以回调指定的函数完成对应功能  
  19.   
  20.     private $masterPid = null;  
  21.   
  22.     private $retry_times = 1;  
  23.   
  24.     private $strReg = '/<div\sclass="result\sc-result\sc-clk-recommend"(.*)?>(.*)?(<img\ssrc="(.*)?">)?(.*)?(<p\sclass="c-line-clamp3\sc-color">(.*)?)+<\/div>/Uis';  
  25.   
  26.     private static $_arrPattern = array(  
  27.         array('name'=>'nature_result''reg'=>'/data-log=\"(.*?)\"/''location'=>1),  
  28.         array('name'=>'title''reg'=>'/<h3(.*?)>(.*?)<\/h3>/''location'=>2),  
  29.         array('name'=>'abstract''reg'=>'/<p class=\"c-line-clamp3 c-color\">(.*?)<\/p>/''location'=>1),  
  30.         array('name'=>'source_url''reg'=>'/<div class=\"c-showurl c-line-clamp1\"><span>(.*?)<\/span>/''location'=>1),  
  31.         array('name'=>'url''reg'=>'/<div class=\"c-container\"><a(.*?)class=\"c-blocka\" href=\"(.*?)\">/''location'=>2),  
  32.         array('name'=>'img''reg'=>'/<div class=\"c-img c-img-s\"><img data-imagedelaysrc=\"(.*?)\"/''location'=>1),  
  33.     );  
  34.   
  35.     public function __construct($strQuery$intPageNum=76) {  
  36.         $this->_strQuery = $strQuery;  
  37.         $this->_intPageNum = $intPageNum;  
  38.     }  
  39.   
  40.     public function execute() {  
  41.         $this->setMasterPid();  
  42.         $this->forkWorker();  
  43.         $this->monitorWorker();  
  44.     }  
  45.   
  46.     private function setMasterPid() {  
  47.         $this->masterPid = posix_getpid();  
  48.     }  
  49.   
  50.     public function setWorkerProcess($intWorkerProcess) {  
  51.         if ($intWorkerProcess <= 0) {  
  52.             return false;  
  53.         }  
  54.         $this->worker_process = $intWorkerProcess;  
  55.     }  
  56.   
  57.     public function setRetryTimes($intTimes) {  
  58.         if ($intTimes <= 0) {  
  59.             return false;  
  60.         }  
  61.         $this->retry_times = $intTimes;  
  62.     }  
  63.   
  64.     public function setRegPattern($strReg) {  
  65.         if (empty($strReg)) {  
  66.             return false;  
  67.         }  
  68.         $this->strReg = $strReg;  
  69.     }  
  70.   
  71.     public function setPattern($arrPattern) {  
  72.         if (!is_array($arrPattern) || empty($arrPattern)) {  
  73.             return false;  
  74.         }  
  75.         self::$_arrPattern[] = $arrPattern;  
  76.     }  
  77.   
  78.     private function monitorWorker() {  
  79.         if ($this->masterPid === posix_getpid()) {  
  80.             foreach ($this->_arrPids as $intPid) {  
  81.                 pcntl_waitpid($intPid$status, WUNTRACED);  
  82.                 $status = pcntl_wexitstatus($status);  
  83.                 if ($status === 100) {  
  84.                     unset($this->_arrPids[$inPid]);  
  85.                 }  
  86.             }  
  87.         }  
  88.     }  
  89.   
  90.     /*主调用方法*/  
  91.     public function forkWorker() {  
  92.   
  93.         for ($i=0; $i<$this->worker_process; ++$i) {  
  94.   
  95.             $pid = pcntl_fork();  
  96.   
  97.             if ($pid === -1) {  
  98.                 exit;  
  99.             } elseif ($pid > 0) {  
  100.                 $this->_arrPids[$pid] = $pid;  
  101.             } else {  
  102.                 $arrResult = $this->run($i);  
  103.                 if ($this->dataHandler) {  
  104.                     call_user_func($this->dataHandler, $arrResult);  
  105.                 }  
  106.                 exit(100);  
  107.             }  
  108.         }  
  109.     }  
  110.   
  111.     /*为worker分配任务*/  
  112.     private function run($intWorkerId) {  
  113.   
  114.         $intPage = ceil($this->_intPageNum / $this->worker_process);  
  115.   
  116.         $intBegin = $intWorkerId * $intPage;  
  117.   
  118.         $intEnd = ($intWorkerId + 1) * $intPage;  
  119.   
  120.         $intEnd = $intEnd > $this->_intPageNum ? $this->_intPageNum : $intEnd;  
  121.   
  122.         for ($i=$intBegin$i<$intEnd; ++$i) {  
  123.   
  124.             $strUrl = 'm.baidu.com/s?word=' . urlencode($this->_strQuery);  
  125.             $strUrl .= $i == 0 ? '' : '&pn=' . $i*10;  
  126.             //如果失败则重试  
  127.             $error_times = 0;  
  128.             while (true) {  
  129.                 if ($error_times >= $this->retry_times) {  
  130.                     break;  
  131.                 }  
  132.                 $strHtml = $this->curl($strUrl);  
  133.                 $arrMatches = $this->getHtmlContent($strHtml);  
  134.                 $arrNaturalResult = $this->getNaturalResult($arrMatches);  
  135.                 if (!empty($arrNaturalResult)) {  
  136.                     $arrResult[$i] = $arrNaturalResult;  
  137.                     break;  
  138.                 }  
  139.                 $error_times++;  
  140.             }  
  141.         }  
  142.         return $arrResult;  
  143.     }  
  144.   
  145.     private function curl($url) {  
  146.   
  147.         $ch = curl_init();  
  148.   
  149.         curl_setopt($ch, CURLOPT_URL, $url);  
  150.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  
  151.         curl_setopt($ch, CURLOPT_TIMEOUT, 10);  
  152.         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);  
  153.   
  154.         $result = curl_exec($ch);  
  155.   
  156.         if (curl_errno($ch)) {  
  157.             exit;  
  158.         }  
  159.   
  160.         return $result;  
  161.     }  
  162.   
  163.     public function getHtmlContent($strHtml) {  
  164.   
  165.         if (empty($strHtml)) {  
  166.             return false;  
  167.         }  
  168.   
  169.         preg_match_all($this->strReg, $strHtml$arrMatches);  
  170.   
  171.         return $arrMatches[0];  
  172.     }  
  173.   
  174.     public function getNaturalResult($arrMatches) {  
  175.   
  176.         if (empty($arrMatches) || !is_array($arrMatches)) {  
  177.             return false;  
  178.         }  
  179.   
  180.         $arrNaturalResult = array();  
  181.   
  182.         foreach ($arrMatches as $key=>$div) {  
  183.   
  184.             foreach (self::$_arrPattern as $val) {  
  185.                 $strName = $val['name'];  
  186.                 $$strName = '';  
  187.             }  
  188.   
  189.             foreach (self::$_arrPattern as $val) {  
  190.   
  191.                 $strName = $val['name'];  
  192.   
  193.                 preg_match_all($val['reg'], $div$matches);  
  194.   
  195.                 if (!isset($matches[$val['location']][0])) {  
  196.                     continue;  
  197.                 }  
  198.   
  199.                 $$strName = isset($matches[$val['location']][0]) ? $matches[$val['location']][0] : '';  
  200.   
  201.                 if ($val['name'] === 'nature_result') {  
  202.   
  203.                     $$strName = str_replace('\'''"', $$strName);  
  204.                     $$strName = json_decode($$strName, true);  
  205.                 } else {  
  206.                     $$strName = strip_tags($$strName);  
  207.                 }  
  208.                 $arrNaturalResult[$key][$val['name']] = $$strName;  
  209.             }  
  210.         }  
  211.   
  212.         return $arrNaturalResult;  
  213.     }  
  214. }  

调用方法:

$obj = new NaturalResultSpider($strQuery, $pageNo);

指定需要抓取什么query的搜索结果,和抓取的页数,最多76页

$obj->setWorkerProcess(4);

指定4个进程进行抓取

$obj->setRetryTimes(3);

抓取失败重试次数

$obj->dataHandler = 'printRes';

指定回调方法进行数据处理

$obj->execute();

以上设置好之后开始运行

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值