使い捨てレベルで、クローラを作らなくちゃならなくなったので、とりあえず file_get_contents でもループして放置するか…とも思ったけれど、せっかくなので同時に4接続ほどするようにしてみた。PHP5には curl_multi_exec という便利なものがあるので、これを使う。
ソース
クローラクラス
<?php class multiCrawler{ var $uriArray = array(); var $output = array(); var $timeout = 0; var $ua = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'; //最大同時接続数 var $accessMaxNum = 4; var $start = 0; //エラーログを出力するかどうか var $errorLog = false; var $errorLogFile = 'crowl_error.txt'; //クロール対象URLをセット function setUri($uri){ $this->uriArray = (is_array($uri))?$uri:array($uri); } //クロールエラーログフラグをセット function setErrorLogFlg($bool){ $this->errorLog = $bool; } //クロールエラーログファイルをセット function setErrorLogFile($file){ $this->errorLogFile = $file; } //タイムアウトをセット function setTimeout($timeout){ $this->timeout = (is_int($timeout))?$timeout:0; } //エラーを出力 function outputError($uri,$error){ $fp = fopen($this->errorLogFile,'a'); fputs($fp,date('Y-m-d H:i:s') . ' ' . $uri . ' : ' . $error."\n"); fclose($fp); } //実行 function execute(){ $this->output = array(); $count = count($this->uriArray); $num = ceil($count / $this->accessMaxNum); for($i=0;$i<$num;$i++){ sleep(1); $this->start = $i * $this->accessMaxNum; $this->end = ($i + 1) * $this->accessMaxNum; $this->fetchUri(); } return $this->output; } //クロール function fetchUri(){ for($i=$this->start;$i<$this->end;$i++){ if(!isset($this->uriArray[$i]) || $this->uriArray[$i] == ''){ $this->end = $i; break; } } for($i=$this->start;$i<$this->end;$i++){ $ch[$i] = curl_init(); curl_setopt($ch[$i],CURLOPT_URL, trim($this->uriArray[$i])); curl_setopt($ch[$i],CURLOPT_HEADER, 0); curl_setopt($ch[$i],CURLOPT_RETURNTRANSFER,1); curl_setopt($ch[$i],CURLOPT_FAILONERROR,1); curl_setopt($ch[$i],CURLOPT_FOLLOWLOCATION,1); curl_setopt($ch[$i],CURLOPT_MAXREDIRS,3); curl_setopt($ch[$i],CURLOPT_SSL_VERIFYPEER,false); curl_setopt($ch[$i],CURLOPT_SSL_VERIFYHOST,false); curl_setopt($ch[$i],CURLOPT_USERAGENT,$this->ua); if($this->timeout){ curl_setopt($ch[$i],CURLOPT_CONNECTTIMEOUT,$this->timeout); curl_setopt($ch[$i],CURLOPT_TIMEOUT,$this->timeout); } } $mh = curl_multi_init(); for($i=$this->start;$i<$this->end;$i++){ curl_multi_add_handle($mh,$ch[$i]); } $active = null; do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } for($i=$this->start;$i<$this->end;$i++){ $this->output[$i] = (curl_error($ch[$i]) == '')?curl_multi_getcontent($ch[$i]):false; if($this->output[$i] === false && $this->errorLog){ $this->outputError($this->uriArray[$i],curl_error($ch[$i])); } curl_multi_remove_handle($mh,$ch[$i]); curl_close($ch[$i]); } curl_multi_close($mh); } }
使ってみる
<?php include('multiCrawler.php'); $uriArray = array( 'http://www.yahoo.co.jp/', 'http://www.google.co.jp/', ); $mc = new multiCrawler; $mc->setUri($uriArray); //$mc->setErrorLogFlg(1); //エラーログ //$mc->setTimeout(10); //タイムアウト設定 $result = $mc->execute(); print_r($result);
ソースを見てもらえれば、そんなに難しいことはやっていないので、カスタマイズも簡単。同時に4URLへ接続し(設定したURLが5つ以上の場合は、1秒間のスリープ)、データをごそっと配列へ入れて返す仕様。
つまり、設定したURLが膨大、もしくはアクセス先のファイルサイズが大きいと、それだけメモリ食う。使う際には、配列に格納じゃなくて、ファイルに書き出しとかすれば良いんじゃないかな。
今度は、RubyかPythonで、もっと柔軟なクローラでも作ってみますか。