首页 > PHP > PHP使用curl进行GET请求和POST请求

PHP使用curl进行GET请求和POST请求

curl 是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP、FTP、TELNET等。PHP安装了curl的扩展之后便可以非常轻松实现http的GET请求和POST请求,还可以实现各种高级功能(例如认证,伪装referer,代理),甚至带证书访问https链接等等。但是,我们开发过程中,常常用于采集数据和发送数据,这里以乐视网的PHP接口代码作为例子。

/** 
 * 发送http请求
 * @param $url 请求地址
 * @param $postFields HTTP方法为POST时的请求参数
 * @return string HTTP请求相应结果
 */
function request_url($url, $postFields = null) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_FAILONERROR, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    //https 请求
    if(strlen($url) > 5 && strtolower(substr($url,0,5)) == 'https') {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    }
    //定义一批浏览器UA
    $browser = array(
        'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
        'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0',
        'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)',
        'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
        'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
        'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
        'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
        'Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36',
    );
    //随机选择一个UA
    $default_browser = $browser[array_rand($browser)];
    curl_setopt($ch, CURLOPT_USERAGENT, $default_browser);
    curl_setopt($ch, CURLOPT_REFERER, $url);
    //判断是否为POST请求
    if (is_array($postFields) && 0 < count($postFields))
    {
        $postBodyString = "";
        $postMultipart = false;
        foreach ($postFields as $k => $v)
        {
            if("@" != substr($v, 0, 1))//判断是不是文件上传
            {   
                $postBodyString .= "$k=" . urlencode($v) . "&"; 
            }
            else//文件上传用multipart/form-data,否则用www-form-urlencoded
            {
                $postMultipart = true;
            }
        }
        unset($k, $v);
        curl_setopt($ch, CURLOPT_POST, true);
        if ($postMultipart)
        {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
        }
        else
        {
            curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString,0,-1));
        }
    }
    $reponse = curl_exec($ch);
    //如果采集失败请用try{}catch(){}捕获
    if (curl_errno($ch))
    {
        throw new Exception(curl_error($ch),0);
    }
    else
    {
        $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        //如果采集失败请用try{}catch(){}捕获
        if (200 !== $httpStatusCode)
        {
            throw new Exception($reponse,$httpStatusCode);
        }
    }
    curl_close($ch);
    return $reponse;
}

这个例子写的非常简洁明了,注意使用GET请求的时候不要传入第二个参数,如果需要参数可以参考http_build_query组合完整的url再调用request_url。

建议在调用的时候使用try{}catch(){}进行捕获,因为受到网络状况的影响请求可能会失败。


此外,curl还支持"多线程"采集,然而这里的多线程指的是IO复用,PHP可以通过curl_multi_exec实现。

/** 
 * 批量多线程发送http请求,不支持post批量请求
 * @param $url_arr 请求地址一维数组
 * @return array 请求相应的一维数组返回值
 */
function request_multi_url($url_arr = array())
{
    //1.初始化,curl是在一个handle里面复用连接的,所以这样就可以复用连接了
    $mh = curl_multi_init();
    $ch = array();
    foreach($url_arr as $i => $url)
    {
        $ch[$i] = curl_init($url);
        curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch[$i], CURLOPT_TIMEOUT, 5);
        $header = array();
        $header[] = 'Cache-Control: max-age=0';
        curl_setopt($ch[$i], CURLOPT_HTTPHEADER, $header);
        //2.循环增加ch句柄到批处理会话mh
        curl_multi_add_handle($mh, $ch[$i]);
    }
    //防卡死写法:执行批处理句柄
    do{
        $mrc = curl_multi_exec($mh, $active);
    }while($mrc == CURLM_CALL_MULTI_PERFORM);
    while($active && $mrc == CURLM_OK)
    {
        if(curl_multi_select($mh) != -1)
        {
            do{
                //3.运行当前cURL句柄的子连接
                $mrc = curl_multi_exec($mh, $active);
            }while($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }
    foreach($url_arr as $i => $url)
    {
        //4.采集数据
        $data[] = curl_multi_getcontent($ch[$i]);
        //5.移除句柄资源
        curl_multi_remove_handle($mh, $ch[$i]);
        //6.关闭cURL会话
        curl_close($ch[$i]);
    }
    //7.关闭一组cURL句柄
    curl_multi_close($mh);
    return $data;
}

多线程请求可以缩短请求时间,但是,不要指望curl多线程请求效率能成倍数提高,因为网络才是关键制约因素。


另外,HTTP除了GET和POST这两种常用的之外,还有HEAD请求方式,该请求方式允许客户端仅向服务器请求某个资源的响应头, 而不要真正的下载该资源本身, 省略了响应体使得请求时间更短。

仔细观察curl请求流程,发现只需省略curl_exec即可,这里以判断远程文件是否存在为例子讲解。

/** 
 * 发送http请求
 * @param $url 请求地址
 * @param $postFields HTTP方法为POST时的请求参数
 * @return string HTTP请求相应结果
 */
function remote_file_exists($filepath)
{
    if(function_exists('curl_init'))
    {
        $curl = curl_init($filepath);                                   // 不取回数据
        curl_setopt($curl, CURLOPT_NOBODY, true);
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');               // 发送请求
        $result = curl_exec($curl);
        $found = false;                                                 // 如果请求没有发送失败
        if($result !== false)
        {
            $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);      //再检查http响应码是否为200
            //如果该资源存在应该会返回200
            if ($statusCode == 200)
            {
                $found = true;
            }
        }
        //不必调用curl_exec,关闭连接
        curl_close($curl);
        return $found;
    }else{
        //如果不存在curl扩展,使用file_get_contents也可以实现head请求
        $fileExists = @file_get_contents($filepath, null, null, -1, 1) ? true : false;
        return $fileExists ? true : false;                              //返回1,就说明文件存在。
    }
}

如果需要PHP获取远程资源的更多详细信息,大可不必自己写函数实现,PHP支持get_headers()可以请求获取http的头部信息。

<?php
$url  =  'http://blog.zhengshuiguang.com' ;
print_r ( get_headers ( $url ));
print_r ( get_headers ( $url ,  1 ));
?>

以上例程的输出类似于:

Array
(
    [0] => HTTP/1.1 200 OK
    [1] => Date: Sat, 29 May 2004 12:28:13 GMT
    [2] => Server: Apache/1.3.27 (Unix)  (Red-Hat/Linux)
    [3] => Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
    [4] => ETag: "3f80f-1b6-3e1cb03b"
    [5] => Accept-Ranges: bytes
    [6] => Content-Length: 438
    [7] => Connection: close
    [8] => Content-Type: text/html
)Array
(
    [0] => HTTP/1.1 200 OK
    [Date] => Sat, 29 May 2004 12:28:14 GMT
    [Server] => Apache/1.3.27 (Unix)  (Red-Hat/Linux)
    [Last-Modified] => Wed, 08 Jan 2003 23:11:55 GMT
    [ETag] => "3f80f-1b6-3e1cb03b"
    [Accept-Ranges] => bytes
    [Content-Length] => 438
    [Connection] => close
    [Content-Type] => text/html
)

我更倾向于get_headers()传入2个参数进行判断,只要匹配到200 OK就说明网络资源存在。

本文地址:http://blog.zhengshuiguang.com/php/curl-http.html

转载随意,但请附上文章地址:-)

标签:curl 采集 get请求 post请求 head请求

相关文章

评论已关闭