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
转载随意,但请附上文章地址:-)
评论已关闭