PHP利用CURL实现多线程获取网页源码方法

 admin   2020-02-04 18:15   871 人阅读  1 条评论

php 利用 Curl 可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等。

然而因为php语言本身不支持多线程,所以开发爬虫程序效率并不高,一般采集数据可以利用 phpQuery类来采集数据库,

在此之外也可以用 Curl ,借助Curl 这个功能实现并发多线程的访问多个url地址以实现并发多线程抓取网页或者下载文件。


如果需要了解curl所有函数可以看这里:函数列表查看

下面是多线程使用到的curl相关函数:

  • curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄

  • curl_multi_close — 关闭一组cURL句柄

  • curl_multi_exec — 运行当前 cURL 句柄的子连接

  • curl_multi_getcontent — 如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流

  • curl_multi_info_read — 获取当前解析的cURL的相关传输信息

  • curl_multi_init — 返回一个新cURL批处理句柄

  • curl_multi_remove_handle — 移除curl批处理句柄资源中的某个句柄资源

  • curl_multi_select — 等待所有cURL批处理中的活动连接

  • curl_multi_setopt — 为 cURL 并行处理设置一个选项

  • curl_multi_strerror — Return string describing error cOD

实现步骤如下:

    1、调用 curl_multi_init,初始化一个批处理handle
    2、循环调用 curl_multi_add_handle,往1中的批处理handle 添加curl_init来的子handle
    3、持续调用 curl_multi_exec,直到所有子handle执行完毕。
    4、根据需要循环调用 curl_multi_getcontent 获取结果
    5、调用 curl_multi_remove_handle,并为每个字handle调用curl_close
    6、调用 curl_multi_close


一般来说,使用多线程curl获取网页源码或下载图片等都是针对多个网址操作的,而不是每次都请求一次,那不如去循环curl_exec。

接下来直接放封装好的函数:

/*
    多线程获取网页源码
    @param array $urls
    @return array
*/

function curl_multi($urls)
{
    if (!is_array($urls) or count($urls) == 0) {
        return false;
    }
    $num = count($urls);
    $curl = $curl2 = $text = array();
    $handle = curl_multi_init();
    function createCh($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Linux; U; Android 4.4.1; zh-cn; R815T Build/JOP40D) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/4.5 Mobile Safari/533.1');
        //设置头部
        curl_setopt($ch, CURLOPT_REFERER, $url); //设置来源
        curl_setopt($ch, CURLOPT_ENCODING, "gzip"); // 编码压缩
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //是否采集301、302之后的页面
        curl_setopt($ch, CURLOPT_MAXREDIRS, 5); //查找次数,防止查找太深
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // 对认证证书来源的检查
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); // 从证书中检查SSL加密算法是否存在
        curl_setopt($ch, CURLOPT_TIMEOUT, 20);
        curl_setopt($ch, CURLOPT_HEADER, 0); //输出头部
        return $ch;
    }
    //准备分配线程
    foreach ($urls as $k => $v) {
        $url = $urls[$k];
        $curl[$k] = createCh($url);
        //向curl批处理会话中添加单独的curl句柄
        curl_multi_add_handle($handle, $curl[$k]);
    }
    $active = null;
    do {
        //运行当前 cURL 句柄的子连接
        $mrc = curl_multi_exec($handle, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);
    
        while ($active && $mrc == CURLM_OK) {
        //等待所有cURL批处理中的活动连接
        if (curl_multi_select($handle) != -1) {
        usleep(100);
        }
        do {
        //运行当前 cURL 句柄的子连接
        $mrc = curl_multi_exec($handle, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
    }
    
    foreach ($curl as $k => $v) {
        if (curl_error($curl[$k]) == "") {
            //如果没有报错则将获取到的字符串添加到数组中
            $text[$k] = (string) curl_multi_getcontent($curl[$k]);
        }
        //移除并关闭curl该句柄资源
        curl_multi_remove_handle($handle, $curl[$k]);
        curl_close($curl[$k]);
    }
    //关闭cURL句柄
    curl_multi_close($handle);
    //将数组返回
    return $text;
}


函数的使用示例:

<?php
    $arr=[
            'http://baidu.com',
            'http://taobao.com',
            'http://jd.com',
            'http://wslmf.com'
        ];
    $res=curl_multi($arr);
    var_dump($res);


到这里基本就结束了。此外,继续简单的封装了单线程curl访问(等同于file_get_contents)一样的函数:

/*
    访问网站源码
    @param string $url
    @return string
*/

function curl_get($url)
{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Linux; U; Android 4.4.1; zh-cn; R815T Build/JOP40D) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/4.5 Mobile Safari/533.1');
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    $content = curl_exec($ch);
    curl_close($ch);
    return ($content);
}

最后,大家尽可能使用curl去爬取网站,因为file_get_contents效率太低了,curl大概能提升40%


2020年2月4日更新curl伪造ip方法及可选post/get提交方式

    /**
     * 请求html页面源码接口数据方法
     * @param string $url 请求地址
     * @param int 访问方式 1:post  0:get 默认 get方式
     * @param string 上传文件
     * @param string cookie文件
     * @param bool 请求头
     * @return string 数据
     */
    function getHtml($url, $ifpost = 0, $datafields = '', $cookiefile = '', $v = false)
    {
        // 伪造IP
        $xforip = rand(1, 254) . "." . rand(1, 254) . "." . rand(1, 254) . "." . rand(1, 254);
        $header = array("Connection: Keep-Alive", "Accept: application/JSon, text/javascript, */*; q=0.01", "Pragma: no-cache", "Accept-Language: zh-Hans-CN,zh-Hans;q=0.8,en-US;q=0.5,en;q=0.3", "User-Agent: Opera/9.80 (windows NT 6.0) Presto/2.12.388 Version/12.14", 'CLIENT-IP:' . $xforip, 'X-FORWARDED-FOR:' . $xforip); // 请求头信息
        $ch = curl_init();
        // 使用伪造代理IP
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER, $v);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        $ifpost && curl_setopt($ch, CURLOPT_POST, $ifpost);
        $ifpost && curl_setopt($ch, CURLOPT_POSTFIELDS, $datafields);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
        $cookiefile && curl_setopt($ch, CURLOPT_COOKIEFILE, $cookiefile);
        $cookiefile && curl_setopt($ch, CURLOPT_COOKIEJAR, $cookiefile);
        $r = curl_exec($ch);
        curl_close($ch);
        return $r;
    }


发表评论:


表情

评论列表

  1. 帅气的孩子
    帅气的孩子  @回复

    6666