首頁 >後端開發 >php教程 >多線程的cURL

多線程的cURL

WBOY
WBOY原創
2016-07-25 08:49:341003瀏覽
本處是一個可以靈活的多執行緒的呼叫 cURL的。
這裡跟php手冊http://us2.php.net/manual/zh/function.curl-multi-select.php 提供的範例不同,程式碼執行效率高不少
本處兩個文件,一個是muti_curl的文件,裡麵包含兩個class
一個是運用的方法,這裡是批次檢查代理ip是否可用
  1. class request_setting {
  2. public $url = false;
  3. public $method = 'GET';
  4. public $ post_data = null;
  5. public $headers = null;
  6. public $options = null;
  7. function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = "GET", $post_data = null, $headers = null, $options = null) {
  8. $this->url = $url;
  9. $this->method = $method;
  10. $this->post_data = $post_data;
  11. $this-> ;headers = $ headers;
  12. $this->options = $options;
  13. }
  14. public function __destruct() {
  15. unset($this->url, $this->method , $this->post_data, $this->headers, $this->options);
  16. }
  17. }
  18. /**************************************************** *******************************************
  19. 批次操作的類
  20. *********************************************** ********************************************/
  21. class muti_curl {
  22. protected $thread_size = 1000🎜>;
  23. ;
  24. ;
  25. ;
  26. ;
  27. ;
  28. ;
  29. ;
  30. ;
  31. ;
  32. ; protected $timeout = 30;
  33. private $callback;
  34. protected $options = array(
  35. CURLOPT_SSL_VERIFYPEER => false,//停用後cURL將終止從服務端進行驗證。選項設定憑證目錄CURLOPT_SSL_VERIFYPEER(預設值為2)啟用,CURLOPT_SSL_VERIFYHOST需要設定為TRUE否則設定為FALSE。 , //將以檔案流的形式傳回curl_exec()所取得的訊息,而不是直接輸出
  36. CURLOPT_CONNECTTIMEOUT =>; 15、
  37. CURLOPT_TIMEOUT => 30,
  38. // CURLOPT_HTTP_VERSION_SION_A_A_SSION_SA_RL_RL_RL_RL_RLS_45S_SP])_RL_RL_A_RLS_RL_RL_RLS4_S]_A_RL_RLS_SH)4A_RL_RL_RL_RL_RLS4_S]_A4_RL_SA_RLS_RL_RL_RLS_RL_RLS_RLS4_S]_A4_RL_SA_RLS_RL_RL_RLS_RL.使用代理的時候用這個去抓取數據,更爽
  39. // CURLOPT_AUTOREFERER=>false,//當根據Location:重定向時,自動header中的Referer:資訊設定。
  40. // CURLOPT_BINARYTRANSFER=>false, //在啟用CURLOPT_RETURNTRANSFER的時候,傳回間歇的(Raw)輸出不需要設定。
  41. // CURLOPT_COOKIESESSION=>true,/ / 啟用時curl會直接輸入一個會話cookie,忽略其他的cookie,預設情況下cURL保留所有的cookie回傳
  42. // CURLOPT_CRLF=>false,// 啟用時將Unix的換行符號轉換成回車
  43. // CURLOPT_DNS_USE_GLOBAL_CACHE=>false, // 啟用時會啟用一個全域的DNS緩存,這樣對於執行緒安全的,並且預設啟用。
  44. // CURLOPT_FAILONERROR=>false, //顯示HTTP狀態碼,預設行為是忽略編號小於等於400的HTTP訊息。
  45. // CURLOPT_FILETIME=>true, // 啟用時會嘗試遠端文件中的資訊。結果資訊會透過curl_getinfo()函數的CURLINFO_FILETIME 選項傳回。 curl_getinfo().
  46. // CURLOPT_FOLLOWLOCATION=>false, // 啟用時指派伺服器伺服器傳回的"Location: "放在header中遞歸的傳回伺服器,使用CURLOPT_MAXREDIRS可以限定遞歸回傳的數量。
  47. // CURLOPT_FORBID_REUSE =>true, //在互動完成後強迫斷開連接,不能重複使用。
  48. // CURLOPT_FRESH_CONNECT=>true,// 強制取得一個新的連接,替代伺服器中的連接。
  49. // CURLOPT_FTP_USE_EPRT=>false,//啟用時當FTP下載時,使用EPRT(或LPRT)。設定為FALSE時指令停用EPRT和LPRT,僅使用PORT指令。
  50. // CURLOPT_FTP_USE_EPSV=>false,// 啟用時,在FTP傳輸過程中回復到PASV模式前先嘗試EPSV指令。設定為FALSE時取消EPSV指令。
  51. // CURLOPT_FTPAPPEND=>false, // 啟用時追加寫入檔案而不是覆寫它。
  52. // CURLOPT_FTPASCII=>false,// CURLOPT_TRANSFERTEXT 的別名。
  53. // CURLOPT_FTPLISTONLY=>false,// 啟用時只匯入FTP目錄
  54. // CURLOPT_HEADER=>true,// 啟用時分割頭檔的資訊作為資料流輸出。 // CURLINFO_HEADER_OUT=>false, // 啟用時追蹤句柄的請求字串// CURLOPT_HTTPGET=>true,// 啟用時會設定HTTP的方法為GET,因為GET是預設是,所以只會是在被修改的情況下使用。 // CURLOPT_HTTPPROXYTUNNEL =>true ,// 啟用時會透過HTTP代理來傳送。 // CURLOPT_MUTE=>true,// 啟用時將cURL函數中所有修改過的參數預設恢復值。 // CURLOPT_NETRC=>false ,// 在連接建立以後,訪問~/.netrc檔案取得使用者名稱和密碼資訊連接遠端站點。 // CURLOPT_NOBODY=>true, 啟用時將不HTML中的BODY部分進行輸出。 / / CURLOPT_NOPROGRESS=>false,//啟用時關閉curl傳輸的進度條,這樣的預設設定為啟用。 // CURLOPT_NOSIGNAL=>false,//啟用時忽略所有的curl傳遞給php進行的訊號。在SAPI多執行緒傳輸時此項目被預設為啟用。 cURL 7.10時被加入。
  55. // CURLOPT_POST=>false,// 啟用時會傳送一個常規的POST請求,類型為:application/x-www-form-urlencoded,就像表單提交的一樣。
  56. // CURLOPT_PUT=>false,// 啟用時允許HTTP傳送文件,必須同時設定CURLOPT_INFILE和CURLOPT_INFILESIZE。
  57. // CURLOPT_TRANSFERTEXT=>false,// 啟用後對FTP傳輸使用ASCII模式。對於LDAP,它檢索純文字資訊而非HTML。在Windows系統上,系統不會把STDOUT設定成binary模式。
  58. // CURLOPT_UNRESTRICTED_AUTH=>true,// 在使用CURLOPT_FOLLOWLOCATION產生的header中的多個locations中持續追加使用者名稱和密碼訊息,即使網域已變更。
  59. // CURLOPT_UPLOAD=>false,// 啟用後允許檔案上傳。
  60. // CURLOPT_VERBOSE =>true,// 啟用時會報告所有的信息,存放在STDERR或指定的CURLOPT_STDERR中。
  61. );
  62. private $headers = array();
  63. private $requests = array();
  64. private $requestMap = array();
  65. /*********************
  66. 建構一個callback的函式
  67. ********************/
  68. $this->callback = $callback;
  69. }
  70. /**************************************************** ****************
  71. 重載__get的方法
  72. ********************** *********************************************/ public ftion __get($名稱) {
  73. return (isset($this->{$name})) ? $this->{$name} : null;
  74. }
  75. /**************************************************** *****************
  76. 重載__set的方法
  77. ********************* **********************************/
  78. public function __set($name, $value) {
  79. // 增加一個設定到headers
  80. if ($name == "options" || $name == "headers") {
  81. $this->{$name} = $value $this->{$name };
  82. } else {
  83. $this->{$name} = $value;
  84. }
  85. return true;
  86. }
  87. // 增加一個請求
  88. public function add($請求) {
  89. $this->requests[] = $request;
  90. return true;
  91. }
  92. public function request($url, $method) = "GET" , $post_data = null, $headers = null, $options = null) {
  93. $this->requests[] = new request_setting($url, $method, $post_data, $headers, $options );
  94. return true;
  95. }
  96. public function get($url, $headers = null, $options = null) {
  97. return $this->request($url, "GET", null, $headers, $options);
  98. }
  99. public function post($url, $post_data = null, $headers = null, $options = null) {
  100. return $this->request( $url, "POST", $post_data, $headers, $options);
  101. }
  102. private function single_curl() {
  103. $ch = curl_init(); //初始化
  104. $request = array_shift($this->requests);//將第一個單元移出並作為結果
  105. $options = $this->get_options($request);//獲得該單元的設定
  106. curl_setopt_array($ch, $options);//批次設定
  107. $output = curl_exec($ch);
  108. $curl_info = curl_getinfo($ch);
  109. $curl_info = curl_getinfo($ch);
  110. if ( $this->callback) {
  111. $callback = $this->callback;
  112. if (is_callable($this->callback)) {
  113. call_user_func($callback, $output, $curl_info, $request );
  114. }
  115. }
  116. else
  117. return $output;
  118. 傳回true;
  119. }
  120. 核武函數 {$ n )($ thull
  121. if ($thread_size){
  122. $this->thread_size = $thread_size;
  123. }
  124. if (count($this->requests)
  125. $this->thread_size) {
  126. $this->thread_size = count($this->requests);
  127. }
  128. if ($this->thread_size $errorinfo = '線程大小必須大於1! !!!';
  129. throw new Exception($errorinfo);
  130. }
  131. $queue = curl_multi_init();
  132. //在執行緒開始增加任務佇列
  133. for (> for (> //在執行緒裡開始增加任務佇列
  134. for (> for (> for (> for (>) $i = 0; $i thread_size;
  135. $ch =curl_init();
  136. $options = $this->get_options($this->requests[$i]) ;
  137. curl_setopt_array($ch, $options) ;//取得設定
  138. curl_multi_add_handle($queue, $ch);//新增佇列
  139. $key = (string) $ch;
  140. $this ->requestMap[ $key] = $i;
  141. }
  142. do {
  143. while (($statu_run_muti_exec = curl_multi_exec($queue, $active)) == CURLM_CALL_MULTI_PERFORM) ;
  144. if ($statc_run_run_MULTI_{Futi_) ;
  145. if ($statc_run_run_m; }
  146. // 發現完成的一個請求,進行處理
  147. while ($done = curl_multi_info_read($queue)) {
  148. $curl_info = curl_getinfo($done['handle']);
  149. $輸出=curl_multi_getcontent($done['handle']);
  150. $callback = $this->callback;
  151. if (is_callable($callback)){
  152. $key = (string) $done[ ' handle'];
  153. $request = $this->requests[$this->requestMap[$key]];
  154. unset($this->requestMap[$key]);//這個內部提示用非常帥
  155. call_user_func($callback, $output, $curl_info, $request);
  156. }
  157. // 增加一個未處理的請求加入到一個已經完成的佇列
  158. if ($i requests) && isset($this->requests[$i]) && $i requests)) {
  159. $ch =curl_init();
  160. $options = $this->get_options($this->requests[$i]);
  161. curl_setopt_array($ch, $options);
  162. curl_multi_add_handle($queue, $ch);
  163. $key = (string ) $ch; $this->requestMap[$key] = $i; $i ; }
  164. curl_multi_remove_handle($queue, $done['handle']);
  165. echo "done ";
  166. print_r($queue);
  167. print_r ($done);
  168. }
  169. // 這一步非常非常重要如果有一個完成後,要重新設定設定timeout的時間
  170. //這裡很關鍵的一點,就是要保證第一次所有的線程裡的請求最少有一個是有效的,否則就出現第一次所有的都沒有效果,導致$active=0,因此不執行下面的東西
  171. if ($active >0){
  172. curl_multi_select($queue, $this->timeout);
  173. }
  174. } while ($active);
  175. curl_multi_close($queue);
  176. return true;
  177. }
  178. public function execute($ullthread_cute($ullthread_
  179. //判斷thread_size的大小如果只有一個請求,就用單線程模式
  180. if (count($this->requests) == 1) {
  181. return $this->single_curl();
  182. } else {
  183. return $this->rolling_curl($thread_size);
  184. }
  185. }
  186. private function get_options($request) {
  187. $options = $this-this>__(' options');
  188. if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
  189. // $options[CURLOPT_FOLLOWLOCATION] = 1;
  190. // $
  191. / $ options[CURLOPT_MAXREDIRS] = 5;
  192. }
  193. $headers = $this->__get('headers');
  194. if ($request->options) {
  195. $options = $options = $ request->options $options;
  196. }
  197. $options[CURLOPT_URL] = $request->url;
  198. //以下分別對post選項與header選項設定
  199. if ($request-> post_data){
  200. $options[CURLOPT_POST] = 1;
  201. $options[CURLOPT_POSTFIELDS] = $request->post_data;
  202. }
  203. if ($headers) { $options[CURLOPT_HTTPHEADER] = $headers;
  204. }
  205. return $options;
  206. }
  207. public function __destruct() {
  208. thread_size, $this->callback, $this->options, $this->headers, $this->requests);
  209. }
  210. }
  211. ?>
複製程式碼
  1. header("content-type:text/html; charset=utf-8");
  2. require("muti_curl_class.php");set_time_limit(0);
  3. $sucesesnum=0;
  4. $good_proxy=array();
  5. function request_callback($response, $info, $request) {
  6. global $ ;
  7. // 下面的正規可以選擇性地顯示回傳的結果
  8. /* if (preg_match("~(.*?)~i", $response, $out )) {
  9. $title = $out[1];
  10. }*/
  11. // echo '
    '.$response .'
    ';
  12. echo '
    ';
  13. //對回應也就是 $response 進行偵測判斷裡面是否有設定的字符,如果有判斷運用該代理成功
  14. if( $response !== false && substr_count($response, 'User -agent: Baiduspider') >=1 ) {
  15. // $result = true;
  16. echo "true
    ";
  17. // echo $request[options][10004];
  18. // print_r ($request->options);
  19. echo $request->options[CURLOPT_PROXY];
  20. $good_proxy[]=$request->options[CURLOPT_PROXY];
  21. }
  22. }
  23. echo '
    the-->'. $sucesesnum.'// print_r ($request);
  24. //echo $request-> url;
  25. $sucesesnum ;
  26. echo "
    ";
  27. }
  28. $params = array_merge($_GET, $_POST); //此處取得傳遞過來的代理ip的位址
  29. $result = $proxy_ip = trim($params['ip']);
  30. $timeout=intval(trim($params['timeout']));
  31. if($timeoutif($timeout>300){$timeout=300;}
  32. $thread_size=intval(trim($params['thread_size']));
  33. if($thread_sizeize if($thread_size>300){$thread_size =300;}
  34. if($proxy_ip == '') {
  35. echo '請輸入IP!!';
  36. return;
  37. }
  38. $replace_arr1 = array(' ', 'qq代理:', 'dn28.com', 'qqip', 'qq代理', 'qqqipip ', '代理ip:', 'ip:', '代理ip','"',"'",'\','/',' ');
  39. $result = str_replace($replace_arr1, array (''), $result);
  40. $result = str_replace(",", "n", $result);
  41. $resArr = explode("n", $result);
  42. foreach( $resArr as $k => $v) {
  43. $posProxy = getPos($v, '@');
  44. if($posProxy===false){
  45. if (!empty($v )){$proxyip_and_port = $v; }
  46. }else{
  47. $proxyip_and_port = substr($v, 0, $posProxy);
  48. }
  49. $newRes[] =trim($proxyip_and_port) ;
  50. }
  51. print_r($newRes);
  52. //die();
  53. $option_setting = array(
  54. CURLOPT_SSL_VERIFYPEER => 0,
  55. CURLOPT_SSL_VERIFYPEER => 0, 5,
  56. CURLOPT_TIMEOUT => 30,
  57. CURLOPT_HEADER=>false,
  58. CURLOPT_PROXY=>'',///這個地方設定代理的位置
  59. );$btime=time();
  60. $rc = new muti_curl("request_callback");
  61. $rc->timeout = $timeout ;
  62. $rc->thread_size = $thread_size;
  63. foreach ($newRes as $v) {
  64. $option_setting[CURLOPT_PROXY]=$v;
  65. $request = new request_ $setting($, $setting( method = "GET", $post_data = null,$header= null, $option_setting);
  66. $rc->add($request);
  67. }
  68. $rc->execute();
  69. $etime=time();
  70. $usedtime=$etime-$btime;
  71. echo 'all'. $sucesesnum.'use'. $usedtime;
  72. echo '
    ';
  73. $good_proxy= array_unique($good_proxy);
  74. $str='';
  75. foreach ($good_proxy as $v){
  76. $str.="'".trim($v)."' ,";
  77. }
  78. $str= str_replace ( ' ' , '' ,$str );
  79. $str = preg_replace('/s /', ' ', $str);
  80. echo $str.'
    ';
  81. var_export ($good_proxy);
  82. //var_dump ($good_proxy);
  83. //*************************************** ************************************************** *********************
  84. //************************ *******只用了一個函數
  85. function parseProxyInfo ( $proxyStr ) {
  86. //$proxyStr = '202.115.207.25:80@HTTP;四川省成都市四川師範大學' ;
  87. $posIp = getPos($proxyStr, ':');
  88. $ip = substr($proxyStr, 0, $posIp);
  89. $posPort = getPos($proxyStr, '@');
  90. $port = substr($proxyStr, $posIp 1, $posPort-$posIp-1);
  91. $posType = getPos($proxyStr, ';');
  92. $type = substr($proxyStr , $posPort 1, $posType-$posPort-1);
  93. $location = substr(strstr($proxyStr, ';'), 1);
  94. return array(
  95. 'ip' => $ ip,
  96. 'port' => $port,
  97. 'type' => $type,
  98. 'location' => $location
  99. );
  100. }
  101. function getPos($haystack, $needle){ return strpos($haystack, $needle);}
  102. 函數check_proxy_is_useful($model, $proxy_info_arr = array()) {
  103. 全域$params, $config;
  104. if($model == 'single') {
  105. $proxy_port = intval(trim($params['port']));
  106. $check_proxy_url = $config['verify_url'];
  107. $proxy_time_out = intval(trim($params['timeout']));
  108. $retry = intval(trim($params['retry']));
  109. $proxy_ip = trim($params['ip']);
  110. $proxy = 新代理( $proxy_ip, $proxy_port, $check_proxy_url, $proxy_time_out, $retry );
  111. // 成功返回string success,失敗返回boolean false
  112. $result = $proxy -> check_proxy();
  113. //var_dump($result);
  114. $proxy_str_success = ''.$proxy_ip.':'.$proxy_port.'@'.'HTTP代理驗證成功! ';
  115. $proxy_str_failed = ''.$proxy_ip.':'.$proxy_port.'@'.'HTTP代理驗證失敗! ';
  116. 回傳$result !== false ? $proxy_str_success : $proxy_str_failed;
  117. } elseif ($model == 'collect') {
  118. $proxy_port = intval(triminfo_ ['port']));
  119. $check_proxy_url = $config['verify_url'];
  120. $proxy_time_out = intval(trim($params['timeout']));
  121. $retry = intval($params['timeout']));
  122. $retry = intval( trim($params['retry']));
  123. $proxy_ip = trim($proxy_info_arr[ 'ip']);
  124. /*echo $proxy_ip.'
    ';
  125. echo $ proxy_port.'
    ';
  126. echo $check_proxy_url.'';
  127. echo $proxy_time_out.'
    ';
  128. echo $retry.'
    ' ;*/
  129. if(!isset($proxy) )) {
  130. $proxy = new proxy( $proxy_ip, $proxy_port, $check_proxy_url, $proxy_time_out, $retry );
  131. }
  132. / / 成功回傳string success,失敗回傳boolean false
  133. $結果= $proxy -> check_proxy();
  134. return $result;
  135. }
  136. }
  137. function getget_single(){
  138. 全域$params, $config;
  139. $proxy_ip = trim($params ['ip']);
  140. if($proxy_ip == '') {
  141. echo '請輸入IP!! ';
  142. return;
  143. }
  144. echo check_proxy_is_useful('single' );
  145. }
  146. function get_proxy_by_collect(){
  147. function get_proxy_by_collect(){
  148. 38 美元, $config; $params['url'] = trim($params['url']);
  149. if($params['url'] == '') {
  150. echo '請輸入url!';
  151. return;
  152. }
  153. //$url = 'http:// www.dn28.com/html/75/n-5175.html';
  154. $con = iconv('GBK', ' UTF-8', file_get_contents($params['url']));
  155. preg_match ('/
(.*)
/s' , $con, $arr);
  • $result = strip_tags ($arr[1], '
    ');
  • $replace_arr1 = array(' ', 'QQ代理:', 'dn28.com ', 'qqip', 'QQ代理', ' qq代理ip', '代理ip:', 'ip:', '代理ip');
  • $result = str_replace($replace_arr1, array('') , $result);
  • //print_r( $arr);
  • $resArr =explode('
    ', $result); //print_r($resArr); echo '

    代理開始批量驗證中,整個整個過程將花費您幾十年的時間。

    ';
  • unset($_SESSION['success_arr']);
  • foreach($resArr as $k => $v) {
  • $newRes[$k] = parseProxyInfo( $v );
  • //print_r($newRes[$k]);
  • /*return;*/
  • $result = check_proxy_is_useful('collect', $newRes[$k]);
  • $ $ proxy_str_success = ''.$newRes[$k]['ip'].':'.$newRes[$k]['port'].'@'.$newRes[$k ]['型別']。 >'.$newRes[$k]['ip'].':'.$newRes[$k]['連接埠'].'@'.$newRes[$k]['類型'].' 代理驗證失敗! > $_SESSION['success_arr'][] = $success_arr[] = $newRes[$k];
  • } else {
  • echo $proxy_str_failed;
  • }
  • echo '
    ';
  • }
  • if(isset($success_arr) && count($success_arr) > 0 ) {
  • save_success_proxy($success_proxy($a) ; > echo '

    [將驗證成功的代理人儲存到本機]   [我要查看歷史資料]

    ';
  • } else {
  • echo '

    [我要看歷史資料]

    ';
  • }
  • //print_r($success_arr);
  • }
  • function get_proxy_by_rule(){
  • 全域$params, $config;
  • $result = $proxy_ip = trim($params['ip']);
  • if($proxy_ip == '') {
  • echo '請輸入IP!!';
  • return;
  • }
  • $replace_arr1 = array(' ', 'QQ代理:', 'dn28.com', 'qqip', 'QQ代理', 'QQ代理ip', '代理ip:', 'ip:', '代理ip');
  • $result = str_replace($replace_arr1, array(''), $ result);
  • $resArr =explode("n", $result);
  • //print_r( $resArr);
  • echo '

    代理開始批次驗證中,整個過程需要花費

    ';
  • unset($_SESSION['success_arr']);
  • foreach($resArr as $k => $v) {
  • $newRes[$k] = parseProxyInfo($v);
  • //print_r($newRes[$k]);
  • /*return;*/
  • $result = check_proxy_is_useful('collect', $newRes[$k]);
  • //var_dump($result);
  • $proxy_str_success = ''.$newRes[$ k] ['ip'].':'.$newRes[$k]['連接埠'].'@'.$newRes[$k]['類型'].' 代理驗證成功! ]['ip'].':'.$newRes[$k]['連接埠'].'@'.$newRes[$k]['類型'].' 代理驗證失敗! > $_SESSION['success_arr'][] = $success_arr[] = $newRes[$k];
  • } else {
  • echo $proxy_str_failed;
  • }
  • echo '
    ';
  • }
  • if(isset($success_arr) && count($success_arr) > 0 ) {
  • save_success_proxy($success_proxy($a) ; > echo '

    [儲存到php格式檔案]   [保存驗證成功的代理到本地電腦] ;  [我要看歷史資料]

    ';
  • } else {
  • echo '

    [我不在乎查看資料歷史]

    ';
  • }
  • }
  • function save_success_proxy($success_arr){
  • global $config;
  • date_default_timezone_set(' PRC');
  • date_default_timezone_set(' PRC'); $v) {
  • $str .= $v['ip'].':'.$ v['連接埠'].'@'.$v['型別' ].';'.$v['位置']."n";
  • }
  • $fp = fopen($config[ 'root_path'].'/success_proxy/'.date('YmdHi') .'.log', 'a ');
  • fwrite($fp, $str);
  • fclose($fp);
  • 取消設定($str);
  • }
  • ?>
  • 複製程式碼
  • 陳述:
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn