最近几天在做一个多搜索引擎关键字排名查询工具,用于及时方便的了解关键词在各大搜索引擎的排名。

在抓取360搜索的时候,发现360搜索每页只支持显示10个搜索结果,如果想获取100个搜索结果数据,就得搜索10次,十分影响用户体验,没有人会为了查询一次关键字排名而愿意等待打开10次的网页时间。

这时我想到了用多线程做并发抓取,正好php curlcurl_multi系列函数能实现此功能。

一、curl_multi系列的函数介绍:
1. curl_multi_init:

用来初始化一个"curl_multi"句柄,然后将多个"curl_init"函数生成的"curl"句柄传递给"curl_multi"句柄;此函数不需要然后参数。

2. curl_multi_add_handle:

"curl_multi_add_handle"函数用来将"curl_init"生成的"curl"句柄添加到上面"curl_multi_init"函数生成的"curl_multi"句柄。"curl_multi_add_handle"函数的第一个参数为"curl_multi"句柄,第二个参数为"curl"句柄。

3. curl_multi_exec:

"curl_multi_exec"用于发起curl_multi请求。"curl_multi_add_handle"函数的第一个参数为"curl_multi"句柄,第二个参数是一个"引用参数",它记录未处理完成的请求数,当第二个参数值变为0时,代表所有的请求都已经处理完成(所有请求成功返回或者到达超时时间)。

4. curl_multi_info_read:

"curl_multi_info_read"函数用来读取curl_multi句柄是否有curl返回信息,如果有则返回最先的"curl返回值(数组形式)",否则返回"false",循环调用此函数,直到其返回"false";"curl_multi_info_read"的参数为"curl_mulit"句柄。

5. curl_multi_getcontent:

当所有curl句柄处理完成,这时我们就可以使用"curl_multi_getcontent"函数读取"curl"的返回内容了。"curl_multi_getcontent"的参数为"curl"句柄。

6. curl_multi_remove_handle:

读取完内容后,使用"curl_multi_remove_handle"函数从"curl_mulit"句柄中移出所有"curl"句柄。"curl_multi_remove_handle"函数的第一个参数为"curl_multi"句柄,第二个参数为"curl"句柄。

7. curl_multi_close:

"curl_multi_close"函数用于关闭"curl_mulit"句柄,释放占用的资源。"curl_multi_close"的参数为"curl_mulit"句柄。

二、curl_multi使用流程:

1、 "curl_multi_init"初始化"curl_multi"句柄;
2、 循环创建并添加"curl"句柄,并用"curl_multi_add_handle"函数将其添加到"curl_multi"句柄;
3、 使用"curl_multi_exec"发起请求,并等待所有请求处理完成;
4、 使用"curl_multi_info_read"函数读取返回值;
5、 使用"curl_multi_getcontent"函数读取返回内容;
6、 使用"curl_multi_remove_handle"函数移除curl句柄;
7、 使用"curl_multi_close"关闭curl_multi句柄。

三、下面是我使用curl_multi多线程并发抓取360搜索返回结果的代码片段:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#多线程并发抓取函数mfetch:
function mfetch($params=array(), $method){
    $mh = curl_multi_init(); #初始化一个curl_multi句柄
    $handles = array();
    foreach($params as $key=>$param){
        $ch = curl_init(); #初始化一个curl句柄
        $url = $param["url"];
        $data = $param["params"];
        if(strtolower($method)==="get"){
           #根据method参数判断是post还是get方式提交数据
            $url = "$url?" . http_build_query( $data ); #get方式
        }else{
            curl_setopt( $ch, CURLOPT_POSTFIELDS, $data ); #post方式
        }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER["HTTP_USER_AGENT"]);
        curl_multi_add_handle($mh, $ch);
        $handles[$ch] = $key;
        #handles数组用来记录curl句柄对应的key,供后面使用,以保证返回的数据不乱序。
    }
    $running = null;
    $curls = array(); #curl数组用来记录各个curl句柄的返回值
    do { #发起curl请求,并循环等等1/100秒,直到引用参数"$running"为0
        usleep(10000);
        curl_multi_exec($mh, $running);
        while( ( $ret = curl_multi_info_read( $mh ) ) !== false ){
#循环读取curl返回,并根据其句柄对应的key一起记录到$curls数组中,保证返回的数据不乱序
            $curls[$handles[$ret["handle"]]] = $ret;
        }
    } while ( $running > 0 );
    foreach($curls as $key=>&$val){
        $val["content"] = curl_multi_getcontent($val["handle"]);
        curl_multi_remove_handle($mh, $val["handle"]); #移除curl句柄
    }
    curl_multi_close($mh); #关闭curl_multi句柄
    ksort($curls);
    return $curls;
}
 
#调用参数:
$keword = "360";
$page = 1;
$params = array();
for($i=0;$i<10;$i++){
    $params[$i] = array(
        "url"=>"http://www.so.com/s",
        "params"=>array('q'=>$keyword,'ie'=>"utf-8",'pn'=>($page-1)*10+$i+1)
    );
}
$ret = mfetch($params, 'GET');
print_r($ret);
常见错误:

1、调用curl_multi系列函数时出现如下错误:

 
1
Fatal error: Call to undefined function curl_multi_init() in 文件.php on line 行号

解答:1、需要php5版本才支持curl_multi系列函数;2.需要开启php curl扩展,windows: 在php.ini中把";extension=php_curl.dll"前面的分号去掉; linux:确认/etc/php.ini或者"/etc/php.d/curl.ini"中有类似"extension=curl.so"的行,并重启apache;

2、调用curl系列函数时出现如下错误:

 
1
Call to undefined function curl_init() in 文件.php on line 行号

解答:同上。

3、用curl抓取网页时,发现网页的中文是乱码:

解答:这是由于被抓取的网页编码与您的网页编码不一致引起的,设置"curl_setopt($ch, CURLOPT_HEADER, 1);"可以返回http头,然后从中查看到类似"Content-Type: text/html; charset=gb2312"的行,从中可以看出其编码为"gb2312"在使用$html = iconv("gb2312", "utf-8//ignore", $html); 将编码转为一致即可。

php curl_multi系列函数实现多线程抓取网页的更多相关文章

  1. PHP利用Curl实现多线程抓取网页和下载文件

    PHP 利用 Curl  可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等,然而因为php语言本身不支持多线程,所以开发爬虫程序效率并不高,一般采集 数据可以利用 PHPquery ...

  2. 转 PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)

    PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)   通过curl_setopt()函数可以方便快捷的抓取网页(采集很方便),curl_setopt 是php的一个 ...

  3. PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)

    通过curl_setopt()函数可以方便快捷的抓取网页(采集很方便),curl_setopt 是php的一个扩展库 使用条件:需要在php.ini 中配置开启.(PHP 4 >= 4.0.2) ...

  4. 通过curl模拟多线程抓取网页(curl_multi_*)

    curl请求多个url,以前都是使用循环来处理.最近发现可以通过curl_multi_*系列函数来模拟多线程.比对一下,发现如果请求的url只有几个,2种方案耗时差不多,但是url比较多,差距就非常明 ...

  5. php curl多线程抓取网页

    PHP 利用 Curl Functions 可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等,受限于php语言本身不支持多线程,所以开发爬虫程序效率并不高,这时候往往需 要借助Cu ...

  6. 简单的使用php多线程抓取网页

    PHP 利用 Curl Functions 可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等,受限于php语言本身不支持多线程,所以开发爬虫程序效率并不高,这时候往往需 要借助Cu ...

  7. curl模拟多线程抓取网页(优化)

    通过上篇文章清楚了通过curl_multi_*函数可以一次请求多个url,但是也留下了问题,就是结果要等所有数据请求结束一起返回,才能逐个处理数据.优化代码,使先成功请求的url先返回处理结果,而不是 ...

  8. python多线程抓取网页信息

    #!/usr/env  python #-*- coding: utf-8  -*- import urllib  import urllib2  import random  import requ ...

  9. Python爬虫练习(多线程,进程,协程抓取网页)

    详情点我跳转 关注公众号"轻松学编程"了解更多. 一.多线程抓取网页 流程:a.设置种子url b.获取区域列表 c.循环区域列表 d.创建线程获取页面数据 e.启动线程 impo ...

随机推荐

  1. Node.js使用rabbitMQ(一)

    目前使用的开发环境主要还是win,所以也只是介绍在win下的rabbitMQ的使用. 一.安装rabbitMQ(Installing on Windows) 关于安装地址,参见:Installing ...

  2. IOS 视频流

    https://github.com/kolyvan/kxmovie   demo 项目

  3. 深入浅出HTTPS基本原理

    基础知识准备:在了解HTTPS的基本原理之前,需要先了解如下的基本知识. 一.什么是HTTPS,TLS,SSL HTTPS,也称作HTTP over TLS.TLS的前身是SSL,TLS 1.0通常被 ...

  4. HTML5学习笔记(十二):JavaScript新增Map和Set

    Map JavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对. 但是JavaScript的对象有个小问题,就是键必须是字符串.但实际上Nu ...

  5. Advanced DataStream API Low-latency Event Time Join

    http://training.data-artisans.com/exercises/eventTimeJoin.html

  6. OpenStack大规模部署详解

    https://blog.csdn.net/karamos/article/details/80130443 0.前言今年的2月22日,OpenStack发布了15个版本Ocata. 走过了7年的发展 ...

  7. IBM研究院找到度量安全性方法:容器与虚拟机,谁更安全?

    https://zhuanlan.zhihu.com/p/40446759 虚拟机比容器更安全吗?你可能会有自己的答案,但IBM研究院发现容器的安全性与虚拟机一样,甚至更加安全. 一般来说,从接口宽度 ...

  8. bash shell(5):if,else,while大小比较

    1.if :else 语句 .if的单分支语法格式: if 条件判断;then 语句1 语句2 …… else 语句1 语句2 …… fi .if的多分支语法格式: if 条件判断:then 语句1 ...

  9. [DIOCP3-IocpTask说明书]基于IOCP引擎的多线程任务的投递和回调处理单元

    [说明] IocpTask是基于Iocp引擎的多线程任务投递和处理单元,可以方便的把任务进行投递到IOCP线程进行统一调度和处理,是模仿QDAC-QWorker的处理方式,支持D7以上的版本. [使用 ...

  10. HA&Federation【转】

    转自:http://blog.csdn.net/tutucute0000/article/details/39756123 从nameNode1.namenode2克隆出namenode3.namen ...