ホームページ >バックエンド開発 >PHPチュートリアル >phpcurl_multi_exec()はWebコンテンツを同時にキャプチャします
phpcurl_multi_exec() は Web コンテンツを同時にキャプチャします
php はシングルスレッド言語なので、速度は Java のようなマルチスレッド言語ほど速くありません。結局のところ、主要な点はここではありません...しかし、php にもそれがあります。独自のマルチスレッド (実際には同時実行) メソッド --curl_multi_exec( ).
curl を使用して Web ページのコンテンツを取得できます (curl を理解していない場合は、簡単な例を参照してください)。ただし、複数の Web ページのコンテンツを同時に取得する場合、この場合、curl_multi_exec() は機能します。
以下は、私が Youku コンテンツをクロールしている例です:
function async_get_url($url_array, $wait_usec = 0) { if (!is_array($url_array)) return false; $wait_usec = intval($wait_usec); $data = array(); $handle = array(); $running = 0; $mh = curl_multi_init(); // multi curl handler $i = 0; foreach($url_array as $url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return don't print curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect curl_setopt($ch, CURLOPT_MAXREDIRS, 7); curl_multi_add_handle($mh, $ch); // 把 curl resource 放進 multi curl handler 裡 $handle[$i++] = $ch; } /* 此做法就可以避免掉 CPU loading 100% 的問題 */ // 參考自: http://www.hengss.com/xueyuan/sort0362/php/info-36963.html do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active and $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } /* // 感謝 Ren 指點的作法. (需要在測試一下) // curl_multi_exec的返回值是用來返回多線程處裡時的錯誤,正常來說返回值是0,也就是說只用$mrc捕捉返回值當成判斷式的迴圈只會運行一次,而真的發生錯誤時,有拿$mrc判斷的都會變死迴圈。 // 而curl_multi_select的功能是curl發送請求後,在有回應前會一直處於等待狀態,所以不需要把它導入空迴圈,它就像是會自己做判斷&自己決定等待時間的sleep()。 /* 讀取資料 */ foreach($handle as $i => $ch) { $content = curl_multi_getcontent($ch); $data[$i] = (curl_errno($ch) == 0) ? $content : false; } /* 移除 handle*/ foreach($handle as $ch) { curl_multi_remove_handle($mh, $ch); } curl_multi_close($mh); return $data; } $url="http://m.youku.com/wap/"; $reg1="/<a\s*href=\"(.*?)version=2\"(.*?)>(.*?)<\/a>/i";//获取视频链接 $reg2="/<img([^>]*)\s*class=\"imgdetail\"\s*src=('|\")([^'\"]+)('|\")/i"; $reg3="<a\s*href=\"(.*?)&format=3gphd\"\s*id=\"click_play\"\s*>"; $reg4= "/<p\s*class=\"videotitle\".*?>.*?<\/p>/i";//获取视频标题(备选) // 创建两个cURL资源 $ch1 = curl_init(); $resultArray=array();//装载所有数据的数组 $ch=array(); //$ch2 = curl_init(); // 指定URL和适当的参数 curl_setopt($ch1, CURLOPT_URL,$url); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch1, CURLOPT_HEADER, 0); $content=curl_exec($ch1); curl_close($ch1); //$content=file_get_contents($url); preg_match_all($reg1, $content,$matches); $video=$matches[0];//首页视频的链接 //print_r($video); foreach ($video as $a=>$key) { $position=strpos($key, "href"); $substring=substr($key, $position+11); $pos=strpos($substring, ">"); $link=substr($substring, 0,$pos-1); $nextUrl[$a]=$url.$link; } //$url_array = array( // 'http://www.google.com', // 'http://www.baidu.com', //); //print_r($nextUrl); //print_r(async_get_url($nextUrl)); //并发获取所有网页的内容 $allData=async_get_url($nextUrl); foreach ($allData as $page) { //获取视频图片 preg_match_all($reg2, $page,$img); $img_arr=$img[0]; foreach ($img_arr as $arr) { $position=strpos($arr, "src"); $sub=substr($arr, $position+5); $pos=strpos($sub, "\""); $last=substr($sub, 0,$pos); } //获取视频高清点播地址 preg_match_all($reg3, $page,$vids); $video_arr=$vids[0]; $vid=$video_arr[0]; $position=strpos($vid, "href"); $v_string=substr($vid, $position+11); $pos=strpos($v_string, "\""); $add=substr($v_string, 0,$pos); $video_url=$url.$add; //获取视频的标题 preg_match_all($reg4, $page,$match); $title=$match[0]; //print_r($er); $r=serialize($title); $position=mb_strpos($r, "</p>"); $sub=substr($r, 0,$position); $pos=mb_strrpos($sub, ">"); $til=substr($sub, $pos+1); //整合到一个数组 $subArray=array('image'=>$last,'video'=>$video_url,'title'=>$til); array_push($resultArray, $subArray); } echo json_encode($resultArray);
async_get_url 関数に焦点を当てています
do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active and $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } }
上の段落が重要なポイントであり、難しい点です。
最初のループで、$mrc == CURLM_CALL_MULTI_PERFORM (-1) は、まだ処理されていないハンドル リソースがあることを示しているため、続行します $mrc =curl_multi_exec($mh, $active)
$mrc と $active はどちらも整数型であることに注意してください。
$mrc== CURLM_OK (0) の場合、リソースはまだあるが、まだ到着していないことを示します。
これは 2 番目のサイクルです:
(その間)まだ到着していないリソースがある場合
(if) cURL バッチ接続にアクティブな接続がある場合、つまり、ハンドルが何らかの処理を行っている場合 (詳細については、PHP マニュアルを参照してください)
(do-while) ハンドル リソースの処理
curl 同時実行処理に関する公式ドキュメントは比較的簡潔なので、理解するために自分でも英語のドキュメントをたくさん確認しました。
皆さんも一緒に進歩していけたら嬉しいです!
参考ドキュメント:
http://technosophos.com/content/php-and-curlmultiexec
http://blog.longwin.com.tw/2009/10/php-multi-thread-curl-2009/