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

在抓取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. photoshop,钢笔工具锚点类型

    以下是钢笔工具绘制出的三种类型的锚点,分别是:无切线的硬锚点.左右切线平行的锚点.左右切线不平行的锚点. 使用转换点工具点击锚点,可以实现 无切线锚点 和 左右切线平行的锚点 之间的转化: 对 左右切 ...

  2. java.lang.RuntimeException: Date pattern must be set for column update_time in the schema of component

    使用Talend open studio  ,从mysql数据源中读取数据,将数据导出到excel文档,另外一个mysql数据库,和一个普通文件,遇到标题的错误,模型如下图所示: tMap将30.7中 ...

  3. RocketMq 学习记录

    最近因为工作需求,领导让我安装一下RocketMQ 这里简单记录一下 这里我的操作系统是centos 6.5 64位 我们看一下官网的RocketMQ安装要求 Prerequisite The fol ...

  4. Ingress 原理及实例

    什么是Ingress 在Kubernetes中,Service和Pod的IP地址只能在集群内部网络中路由,所有到达“边界路由器”(Edge Router)的网络流量要么被丢弃,要么被转发到别处,从概念 ...

  5. eclipse CDT写c++使用文件作为输入源(输入重定向)

    在main函数第一句添加下面. freopen("inputfile","r",stdin); 创建一个inputfile,放project根文件夹下. 注意添 ...

  6. bestcoder 48# wyh2000 and a string problem (水题)

    wyh2000 and a string problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K ...

  7. express应用中常用中间件介绍

    var strftime = require('strftime'); 时间格式化中间件,功能和moment.js差不多 var methodOverride = require('method-ov ...

  8. pandas数组(pandas Series)-(4)NaN的处理

    上一篇pandas数组(pandas Series)-(3)向量化运算里说到,将两个 pandas Series 进行向量化运算的时候,如果某个 key 索引只在其中一个 Series 里出现,计算的 ...

  9. Splunk Web页面的登录密码忘记了怎么办

    splunk的web登录密码忘记的话,可以使用以下方法重置. 一.关闭splunk服务 /opt/splunk/bin/splunk stop 二.删除默认密码配置文件 三.重启启动splunk服务, ...

  10. javascript基础拾遗(六)

    1.Date内置对象 获取系统时间 var now = new Date() console.log(now) console.log(now.getDate()) console.log(now.g ...