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

在抓取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. struts2:OGNL表达式之#、%、$符号运用

    1. OGNL表达达符号"#" 1.1 #用于访问OGNL上下文和Action上下文,#相当于ActionContext.getContext() 注意:当系统创建了Action实 ...

  2. IOS开发----生成静态库(.a)

    由于iPhone控件的极度匮乏和自定义组件在重用上的限制,在过去的项目中我们积累了大量的“纯代码”组件——因为IB本身的限制,我们无法把这些组件封装为IB组件库(本来我们想通过分发xib文件的方式重用 ...

  3. 【Unity】4.6 灯光

    分类:Unity.C#.VS2015 创建日期:2016-04-11 一.简介 灯光(Light,也叫光源)是每一个场景的重要组成部分,用于照亮场景和对象,从而让游戏具有自己的个性和风格,比如利用灯光 ...

  4. scikit-learn:6. Strategies to scale computationally: bigger data

    參考:http://scikit-learn.org/stable/modules/scaling_strategies.html 对于examples.features(或者两者)数量非常大的情况, ...

  5. nginx配置长连接

    http { keepalive_timeout 20; --长连接timeout keepalive_requests 8192; --每个连接最大请求数 } events { worker_con ...

  6. 如何使用ILSpy 把发布版本反编译成源码

    有时候,看法别人写的代码比较好,想看看他们的代码到底是如何写的,于是就找方法,看看能否把发布版本变成源码.后来终于发现一个词“反编译”,我终于知道怎么办了. 工具:ILSpy   百度下载一个,该工具 ...

  7. javascript基础拾遗(五)

    1.什么是箭头函数 ES6引入的一种新的函数,类似匿名函数,x=>xx 箭头左端为函数参数,右端为函数体 相当于 function (x){ retutn xx } 2.箭头函数的特点 更简洁 ...

  8. (转) Lua: 给 Redis 用户的入门指导

    可能你已经听说过Redis 中嵌入了脚本语言,但是你还没有亲自去尝试吧?  这个入门教程会让你学会在你的Redis 服务器上使用强大的lua语言. Hello, Lua! 我们的第一个Redis Lu ...

  9. uwsgi -- unavailable modifier requested: 0 -- 的解决办法

    nginx 报错 upstream prematurely closed connection while reading response header from upstream 说明是上游服务器 ...

  10. 【C/C++】程序如何来,编译全过程

    概述    编译的目的是把人书写的高级语言代码翻译成目标程序的语言处理程序,编译用的程序(例如gcc)称为编译系统. 一个编译系统把一个源程序翻译成目标程序的工作过程分为5个阶段:词法分析.语法分析. ...