Home >Backend Development >PHP Tutorial >Multi-threaded cURL

Multi-threaded cURL

WBOY
WBOYOriginal
2016-07-25 08:49:341003browse
This is a flexible multi-threaded call to cURL.
This is different from the sample provided in the PHP manual http://us2.php.net/manual/zh/function.curl-multi-select.php. The code execution efficiency is much higher
There are two files here, one It is a file of muti_curl, which contains two classes
一个是运用的方法,这里是批量检查代理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 = 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. Classes for batch operations
  20. * *************************************************** ****************************************/
  21. class muti_curl {
  22. protected $thread_size = 100;
  23. protected $timeout = 30;
  24. private $callback;
  25. protected $options = array(
  26. CURLOPT_SSL_VERIFYPEER => false,//禁用后cURL将终止从服务端进行验证。使用CURLOPT_CAINFO选项设置证书使用CURLOPT_CAPATH选项设置证书目录 如果CURLOPT_SSL_VERIFYPEER(默认值为2)被启用,CURLOPT_SSL_VERIFYHOST需要被设置成TRUE否则设置为FALSE。 自cURL 7.10开始默认为TRUE。从cURL 7.10开始默认绑定安装。
  27. CURLOPT_RETURNTRANSFER => true, //将 curl_exec()获取的信息以文件流的形式返回,而不是直接输
  28. CURLOPT_CONNECTTIMEOUT => 15,
  29. CURLOPT_TIMEOUT => 30,
  30. // CURLOPT_HTTP_VERSION=>CURL_HTTP_VERSION_1_0, //使用代理的时候用这个去抓取数据,更爽
  31. // CURLOPT_AUTOREFERER=>false,// 当根据Location:重定向时,自动设置header中的Referer:信息。
  32. // CURLOPT_BINARYTRANSFER=>false, //在启用CURLOPT_RETURNTRANSFER的时候,返回原生的(Raw)输出这个不用设置。
  33. // CURLOPT_COOKIESESSION=>true,// 启用时curl会仅仅传递一个session cookie,忽略其他的cookie,默认状况下cURL会将所有的cookie返回
  34. // CURLOPT_CRLF=>false,// 启用时将Unix的换行符转换成回车换行符。
  35. // CURLOPT_DNS_USE_GLOBAL_CACHE=>false, // 启用时会启用一个全局的DNS缓存,此项为线程安全的,并且默认启用。
  36. // CURLOPT_FAILONERROR=>false, // 显示HTTP状态码,默认行为是忽略编号小于等于400的HTTP信息。
  37. // CURLOPT_FILETIME=>true, //启用时会尝试修改远程文档中的信息。结果信息会通过 curl_getinfo()函数的CURLINFO_FILETIME选项返回。 curl_getinfo().
  38. // CURLOPT_FOLLOWLOCATION=>false, // 启用时会将服务器服务器返回的"Location: "放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。
  39. // CURLOPT_FORBID_REUSE=>true, //在完成交互以后强迫断开连接,不能重用。
  40. // CURLOPT_FRESH_CONNECT=>true,// 强制获取一个新的连接,替代缓存中的连接。
  41. // CURLOPT_FTP_USE_EPRT=>false,// 启用时当FTP下载时,使用EPRT (或 LPRT)命令。设置为FALSE时禁用EPRT和LPRT,使用PORT命令 only.
  42. // CURLOPT_FTP_USE_EPSV=>false,// 启用时,在FTP传输过程中回复到PASV模式前首先尝试EPSV命令。设置为FALSE时禁用EPSV命令。
  43. // CURLOPT_FTPAPPEND=>false,// 启用时追加写入文件而不是覆盖它。
  44. // CURLOPT_FTPASCII=>false,// CURLOPT_TRANSFERTEXT的别名。
  45. // CURLOPT_FTPLISTONLY=>false,// 启用时只列出FTP目录的名字。
  46. // CURLOPT_HEADER=>true,// 启用时会将头文件的信息作为数据流输出。
  47. // CURLINFO_HEADER_OUT=>false, //启用时追踪句柄的请求字符串。
  48. // CURLOPT_HTTPGET=>true,// 启用时会设置HTTP的method为GET,因为GET是默认是,所以只在被修改的情况下使用。
  49. // CURLOPT_HTTPPROXYTUNNEL =>true,// 启用时会通过HTTP代理来传输。
  50. // CURLOPT_MUTE=>true,// 启用时将cURL函数中所有修改过的参数恢复默认值。
  51. // CURLOPT_NETRC=>false,// 在连接建立以后,访问~/.netrc文件获取用户名和密码信息连接远程站点。
  52. // CURLOPT_NOBODY=>true, 启用时将不对HTML中的BODY部分进行输出。
  53. // CURLOPT_NOPROGRESS=>false,//启用时关闭curl传输的进度条,此项的默认设置为启用。
  54. // CURLOPT_NOSIGNAL=>false,// 启用时忽略所有的curl传递给php进行的信号。This item is enabled by default during SAPI multi-threaded transmission. Added in cURL 7.10.
  55. // CURLOPT_POST=>false,// When enabled, a regular POST request will be sent, type: application/x-www-form-urlencoded, just like form submission.
  56. // CURLOPT_PUT=>false,// Allow HTTP to send files when enabled, CURLOPT_INFILE and CURLOPT_INFILESIZE must be set at the same time.
  57. // CURLOPT_TRANSFERTEXT=>false,// When enabled, use ASCII mode for FTP transmission. For LDAP, it retrieves plain text information rather than HTML. On Windows systems, the system does not set STDOUT to binary mode.
  58. // CURLOPT_UNRESTRICTED_AUTH=>true,// Continuously append username and password information to multiple locations in the header generated using CURLOPT_FOLLOWLOCATION, even if the domain name has changed.
  59. // CURLOPT_UPLOAD=>false,// Allow file upload when enabled.
  60. // CURLOPT_VERBOSE =>true,// When enabled, all information will be reported and stored in STDERR or the specified CURLOPT_STDERR.
  61. );
  62. private $headers = array();
  63. private $requests = array();
  64. private $requestMap = array();
  65. /*********************
  66. Construct a callback function
  67. ********************/
  68. function __construct($callback = null) {
  69. $this->callback = $callback;
  70. }
  71. /***************************************************** ******************
  72. Overloading the __get method
  73. **************************** *******************************************/
  74. public function __get($name) {
  75. return (isset($this->{$name})) ? $this->{$name} : null;
  76. }
  77. /***************************************************** *******************
  78. Overloading the __set method
  79. ************************* ******************************/
  80. public function __set($name, $value) {
  81. // 增加一个设置到headers
  82. if ($name == "options" || $name == "headers") {
  83. $this->{$name} = $value + $this->{$name};
  84. } else {
  85. $this->{$name} = $value;
  86. }
  87. return true;
  88. }
  89. //增加一个请求
  90. public function add($request) {
  91. $this->requests[] = $request;
  92. return true;
  93. }
  94. public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
  95. $this->requests[] = new request_setting($url, $method, $post_data, $headers, $options);
  96. return true;
  97. }
  98. public function get($url, $headers = null, $options = null) {
  99. return $this->request($url, "GET", null, $headers, $options);
  100. }
  101. public function post($url, $post_data = null, $headers = null, $options = null) {
  102. return $this->request($url, "POST", $post_data, $headers, $options);
  103. }
  104. private function single_curl() {
  105. $ch = curl_init(); //初始化
  106. $request = array_shift($this->requests);//把第一个单元移出并作为结果
  107. $options = $this->get_options($request);//获得该单元的设置
  108. curl_setopt_array($ch, $options);//批设置
  109. $output = curl_exec($ch);
  110. $curl_info = curl_getinfo($ch);
  111. if ($this->callback) {
  112. $callback = $this->callback;
  113. if (is_callable($this->callback)) {
  114. call_user_func($callback, $output, $curl_info, $request);
  115. }
  116. }
  117. else
  118. return $output;
  119. return true;
  120. }
  121. private function rolling_curl($thread_size = null) {
  122. if ($thread_size){
  123. $this->thread_size = $thread_size;
  124. }
  125. if (count($this->requests) < $this->thread_size){
  126. $this->thread_size = count($this->requests);
  127. }
  128. if ($this->thread_size < 2) {
  129. $errorinfo = '线程大小必须大于 1!!!!';
  130. throw new Exception($errorinfo);
  131. }
  132. $queue = curl_multi_init();
  133. //在线程里开始增加任务队列
  134. for ($i = 0; $i < $this->thread_size; $i++) {
  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 ($statu_run_muti_exec != CURLM_OK){ break; }
  145. // 发现完成的一个请求,进行处理
  146. while ($done = curl_multi_info_read($queue)) {
  147. $curl_info = curl_getinfo($done['handle']);
  148. $output = curl_multi_getcontent($done['handle']);
  149. $callback = $this->callback;
  150. if (is_callable($callback)){
  151. $key = (string) $done['handle'];
  152. $request = $this->requests[$this->requestMap[$key]];
  153. unset($this->requestMap[$key]);//这个销毁变量用得很帅
  154. call_user_func($callback, $output, $curl_info, $request);
  155. }
  156. //增加一个未处理的请求加入到一个已经完成的队列中
  157. if ($i < count($this->requests) && isset($this->requests[$i]) && $i < count($this->requests)) {
  158. $ch = curl_init();
  159. $options = $this->get_options($this->requests[$i]);
  160. curl_setopt_array($ch, $options);
  161. curl_multi_add_handle($queue, $ch);
  162. $key = (string) $ch;
  163. $this->requestMap[$key] = $i;
  164. $i++;
  165. }
  166. curl_multi_remove_handle($queue, $done['handle']);
  167. echo "done ";
  168. print_r($queue);
  169. print_r ($done);
  170. }
  171. // This step is very, very important if there is a completion , to reset the timeout setting
  172. //The key point here is to ensure that at least one request in all threads is valid for the first time, otherwise all the requests for the first time will have no effect, resulting in $ active=0, so the following is not executed
  173. if ($active >0){
  174. curl_multi_select($queue, $this->timeout);
  175. }
  176. } while ($active);
  177. curl_multi_close($queue );
  178. return true;
  179. }
  180. public function execute($thread_size = null) {
  181. //Judge the size of thread_size. If there is only one request, use single-threaded mode
  182. if (count($this->requests) == 1) {
  183. return $this->single_curl();
  184. } else {
  185. return $this->rolling_curl($thread_size);
  186. }
  187. }
  188. private function get_options($request) {
  189. $options = $this ->__get('options');
  190. if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
  191. // $options[CURLOPT_FOLLOWLOCATION] = 1;
  192. // $ options[CURLOPT_MAXREDIRS] = 5;
  193. }
  194. $headers = $this->__get('headers');
  195. if ($request->options) {
  196. $options = $request->options + $options ;
  197. }
  198. $options[CURLOPT_URL] = $request->url;
  199. //Set the post options and header options respectively below
  200. if ($request->post_data){
  201. $options[CURLOPT_POST] = 1;
  202. $options[CURLOPT_POSTFIELDS] = $request->post_data;
  203. }
  204. if ($headers) {
  205. $options[CURLOPT_HEADER] = 0;
  206. $options[CURLOPT_HTTPHEADER] = $headers;
  207. }
  208. return $options;
  209. }
  210. public function __destruct() {
  211. unset($this->thread_size, $this->callback, $this->options, $this->headers, $this->requests);
  212. }
  213. }
  214. ?>
Copy code
  1. header("content-type:text/html; charset=utf-8");
  2. require("muti_curl_class.php");
  3. set_time_limit(0);
  4. $sucesesnum=0;
  5. $good_proxy=array();
  6. function request_callback($response, $info, $request) {
  7. global $sucesesnum,$good_proxy;
  8. // The following regular expression can selectively display the returned results
  9. /* if ( preg_match("~(.*?)~i", $response, $out)) {
  10. $title = $out[1];
  11. }*/
  12. // echo '< ;br>'.$response .'
    ';
  13. echo '
    ';
  14. //Check the response, which is $response, to determine whether there are set characters in it, and if so, use the agent Success
  15. if( $response !== false && substr_count($response, 'User-agent: Baiduspider') >=1 ) {
  16. // $result = true;
  17. echo "true
    ";
  18. // echo $request[options][10004];
  19. // print_r ($request->options);
  20. echo $request->options[CURLOPT_PROXY];
  21. $good_proxy[]=$request->options[CURLOPT_PROXY] ;
  22. }
  23. echo '
    the-->'. $sucesesnum.'<---use:'. $info['total_time'];
  24. // print_r ($request);
  25. // echo $request->url;
  26. $sucesesnum++;
  27. echo "
    ";
  28. }
  29. $params = array_merge($_GET, $_POST); //Get the address of the passed proxy ip here
  30. $ result = $proxy_ip = trim($params['ip']);
  31. $timeout=intval(trim($params['timeout']));
  32. if($timeout<3 ){$timeout=3;}
  33. if($timeout>300){$timeout=300;}
  34. $thread_size=intval(trim($params['thread_size']));
  35. if($thread_size<5){$thread_size =5;}
  36. if( $thread_size>300){$thread_size =300;}
  37. if($proxy_ip == '') {
  38. echo 'Please enter IP!!';
  39. return;
  40. }
  41. $replace_arr1 = array(' ', 'qq proxy:', 'dn28.com', 'qqip', 'qq proxy', 'qq proxy ip', 'Agent ip:', 'ip:', 'Agent ip','"',"'" ,'\','/',' ');
  42. $result = str_replace($replace_arr1, array(''), $result);
  43. $result = str_replace(",", "n", $result);
  44. $resArr = explode("n", $result);
  45. foreach($resArr as $k => $v) {
  46. $posProxy = getPos($v, '@');
  47. if($posProxy== =false){
  48. if (!empty($v)){$proxyip_and_port = $v; }
  49. }else{
  50. $proxyip_and_port = substr($v, 0, $posProxy);
  51. }
  52. $newRes[] =trim ($proxyip_and_port);
  53. }
  54. print_r($newRes);
  55. //die();
  56. $option_setting = array(
  57. CURLOPT_SSL_VERIFYPEER => 0,
  58. CURLOPT_RETURNTRANSFER => true,
  59. CURLOPT_CONNECTTIMEOUT => 5,
  60. CURLOPT_TIMEOUT => 30,
  61. CURLOPT_HEADER=>false,
  62. CURLOPT_PROXY=>'',//This is where the proxy is set
  63. );
  64. $url= 'http://www.baidu.com/robots.txt' ;
  65. $btime=time();
  66. $rc = new muti_curl("request_callback");
  67. $rc->timeout = $timeout;
  68. $rc->thread_size = $thread_size;
  69. foreach ($newRes as $ v) {
  70. $option_setting[CURLOPT_PROXY]=$v;
  71. $request = new request_setting($url, $method = "GET", $post_data = null,$header= null, $option_setting);
  72. $rc-> add($request);
  73. }
  74. $rc->execute();
  75. $etime=time();
  76. $usedtime=$etime-$btime;
  77. echo 'all'. $sucesesnum.'use'. $ usedtime;
  78. echo '
    ';
  79. $good_proxy= array_unique($good_proxy);
  80. $str='';
  81. foreach ($good_proxy as $v){
  82. $str.="'".trim($ v)."',";
  83. }
  84. $str= str_replace ( ' ' , '' ,$str );
  85. $str = preg_replace('/s+/', ' ', $str);
  86. echo $str. '
    ';
  87. var_export ($good_proxy);
  88. //var_dump ($good_proxy);
  89. //************* *************************************************** *************************************
  90. //********** *********************Only one function is used
  91. function parseProxyInfo ( $proxyStr ) {
  92. //$proxyStr = '202.115.207.25:80@HTTP; Sichuan Normal University, Chengdu, Sichuan Province';
  93. $posIp = getPos($proxyStr, ':');
  94. $ip = substr($proxyStr, 0, $posIp);
  95. $posPort = getPos($proxyStr, '@' );
  96. $port = substr($proxyStr, $posIp+1, $posPort-$posIp-1);
  97. $posType = getPos($proxyStr, ';');
  98. $type = substr($proxyStr, $posPort +1, $posType-$posPort-1);
  99. $location = substr(strstr($proxyStr, ';'), 1);
  100. return array(
  101. 'ip' => $ip,
  102. 'port' = > $port,
  103. 'type' => $type,
  104. 'location' => $location
  105. );
  106. }
  107. function getPos($haystack, $needle){
  108. return strpos($haystack, $needle );
  109. }
  110. function check_proxy_is_useful($model, $proxy_info_arr = array()) {
  111. global $params, $config;
  112. if($model == 'single') {
  113. $proxy_port = intval(trim($params['port']));
  114. $check_proxy_url = $config['verify_url'];
  115. $proxy_time_out = intval(trim($params['timeout']));
  116. $retry = intval(trim($params['retry']));
  117. $proxy_ip = trim($params['ip']);
  118. $proxy = new proxy( $proxy_ip, $proxy_port, $check_proxy_url, $proxy_time_out, $retry );
  119. //成功返回string success, 失败返回boolean false
  120. $result = $proxy -> check_proxy();
  121. //var_dump($result);
  122. $proxy_str_success = ''.$proxy_ip.':'.$proxy_port.'@'.'HTTP 代理验证成功!';
  123. $proxy_str_failed = ''.$proxy_ip.':'.$proxy_port.'@'.'HTTP 代理验证失败!';
  124. return $result !== false ? $proxy_str_success : $proxy_str_failed;
  125. } elseif ($model == 'collect') {
  126. $proxy_port = intval(trim($proxy_info_arr['port']));
  127. $check_proxy_url = $config['verify_url'];
  128. $proxy_time_out = intval(trim($params['timeout']));
  129. $retry = intval(trim($params['retry']));
  130. $proxy_ip = trim($proxy_info_arr['ip']);
  131. /*echo $proxy_ip.'
    ';
  132. echo $proxy_port.'
    ';
  133. echo $check_proxy_url.'
    ';
  134. echo $proxy_time_out.'
    ';
  135. echo $retry.'
    ';*/
  136. if(!isset($proxy)) {
  137. $proxy = new proxy( $proxy_ip, $proxy_port, $check_proxy_url, $proxy_time_out, $retry );
  138. }
  139. //成功返回string success, 失败返回boolean false
  140. $result = $proxy -> check_proxy();
  141. return $result;
  142. }
  143. }
  144. function get_single(){
  145. global $params, $config;
  146. $proxy_ip = trim($params['ip']);
  147. if($proxy_ip == '') {
  148. echo '请输入IP!!';
  149. return;
  150. }
  151. echo check_proxy_is_useful('single');
  152. }
  153. function get_proxy_by_collect(){
  154. global $params, $config;
  155. $params['url'] = trim($params['url']);
  156. if($params['url'] == '') {
  157. echo '请输入url!';
  158. return;
  159. }
  160. //$url = 'http://www.dn28.com/html/75/n-5175.html';
  161. $con = iconv('GBK', 'UTF-8', file_get_contents($params['url']));
  162. 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]['type'].' 代理验证成功!   IP地址:'.$newRes[$k]['location'].'';
  • $proxy_str_failed = ''.$newRes[$k]['ip'].':'.$newRes[$k]['port'].'@'.$newRes[$k]['type'].' 代理验证失败!   IP地址:'.$newRes[$k]['location'].'';
  • if($result !== false ){
  • echo $proxy_str_success;
  • $_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_arr);
  • echo '

    [保存验证成功的代理到本地电脑]   [我要看看历史数据]

    ';
  • } else {
  • echo '

    [我要看看历史数据]

    ';
  • }
  • //print_r($success_arr);
  • }
  • function get_proxy_by_rule(){
  • global $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]['port'].'@'.$newRes[$k]['type'].' 代理验证成功!   IP地址:'.$newRes[$k]['location'].'';
  • $proxy_str_failed = ''.$newRes[$k]['ip'].':'.$newRes[$k]['port'].'@'.$newRes[$k]['type'].' 代理验证失败!   IP地址:'.$newRes[$k]['location'].'';
  • if($result !== false ){
  • echo $proxy_str_success;
  • $_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_arr);
  • echo '

    [保存到php格式文件]   [保存验证成功的代理到本地电脑]   [我要看看历史数据]

    ';
  • } else {
  • echo '

    [我要看看历史数据]

    ';
  • }
  • }
  • function save_success_proxy($success_arr){
  • global $config;
  • date_default_timezone_set('PRC');
  • $str = '';
  • foreach($success_arr as $k => $v) {
  • $str .= $v['ip'].':'.$v['port'].'@'.$v['type'].';'.$v['location']."n";
  • }
  • $fp = fopen($config['root_path'].'/success_proxy/'.date('YmdHi').'.log', 'a+');
  • fwrite($fp, $str);
  • fclose($fp);
  • unset($str);
  • }
  • ?>
  • 复制代码


    Statement:
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
    Previous article:Automatic file backup softwareNext article:Automatic file backup software

    Related articles

    See more