CURL使⽤⽅法详解
php采集神器CURL使⽤⽅法详解
作者:佚名更新时间:2016-10-21
对于做过数据采集的⼈来说,cURL⼀定不会陌⽣。虽然在PHP中有file_get_contents函数可以获取远程链接的数据,但是它的可控制性太差了,对于各种复杂情况的采集情
景,file_get_contents显得有点⽆能为⼒。因此,本⽂将为你介绍采集神器cURL的使⽤。
先给⼤家补充⼀下file_get_contents函数可以获取远程链接数据的⽅法。
<?php
$url = "git.oschina/yunluo/API/raw/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$notice = curl_exec($ch);
echo $notice;
>
这段代码会直接使⽤curl显⽰⽂件内容,但是问题来了,因为curl是php的扩展,有的主机为了安全会⾦庸curl的,宁外php本地调试的时候也是关闭curl的,所以会发⽣报错,所以这段代码是不可取的,所以云落对他重新改写了
<?php
if (function_exists('curl_init')) {
$url = "git.oschina/yunluo/API/raw/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$dxycontent = curl_exec($ch);
echo $dxycontent;
} else {
echo '汗!貌似您的服务器尚未开启curl扩展,⽆法收到来⾃云落的通知,请联系您的主机商开启,本地调试请⽆视';
}
>
修改后的版本是对curl扩展做⼀个判断,看看服务器到底有⽊有打开curl扩展,如果打开了,就直接显⽰⽂件,如果没打开就显⽰⼀段提⽰⽂字。
虽然修复了问题,但是⼜有⼀个问题来了,我只是显⽰⼀段⽂字⽽已,我也不是是⽤什么做什么⼤事
的,所以我为什么要写那么多的代码呢??
经过⼀些瞎掰的检测,发现file_get_contents获取远程⽂件内容的速度不⽐curl慢,在⼀些⽂件较少的情况下可能还⽐curl扩展要快得多,所以我⼜重写了代码
<?php echo file_get_contents( "git.oschina/yunluo/API/raw/" ); ?>
⼯具
⽕狐浏览器(FireFox) + Firebug
“⼯欲善其事,必先利其器。” 在分析案例之前,先让我们学习⼀下如何利⽤神器Firebug获取我们必要的信息。
使⽤F12打开Firebug,我们可以得到如图(⼀)界⾯:
1、箭头图标是“元素选择”⼯具,单击⼀次会⾼亮图标,同时,⿏标在页⾯内的移动会同时在HTML菜单中选定相应的内容,此时单击内容则表⽰选定了该元素,图标⾼亮取消。如图(⼆)所⽰:
Firebug查看元素
2、控制台
JS⾥⾯的console.log系列函数的打印就是在这⾥输出。
3、HTML
HTML内容,注意这⾥看到的不⼀定是采集要解析的内容,采集时候对内容的分析,⼀律以查看源码(Ctrl+U)为准,这⾥只是能快速定位元素的结构,然后再选择⼀个⽐较特殊的参照,在源码中定位相应的位置。
⽐如,你在HTML⾥⾯看到⼀个标签是<div id="demo" class="demo">Demo</div>,但是你查看源码时候看到的内容可能是<div class="demo" id="demo">Demo</div>,如果你对采集内容按照前者去做正则匹配,那么你会得不到结果。
4、CSS
这⾥是CSS⽂件内容
5、脚本
这⾥是Javascript⽂件内容
6、DOM
Dom节点内容
7、⽹络
每⼀个请求链接的数据,这⾥是我们采集要关注和分析的地⽅,它能够显⽰每⼀个请求的参数、请求头、Cookie数据等。在页⾯提交会刷新的情况下,需要使⽤保持,使得页⾯请求内容在刷新后仍然留着控制台中,如图(三)所⽰:
另外,⽕狐还有⼀款 Tamper data 扩展也能得到请求数据,必要时可以安装使⽤。
8、Cookies
Cookie数据
在图(⼀)中还看到下⾯有很多可选的⼩菜单项,其中保持是我们要关注的,当选择它的时候,即使提交表单刷新了页⾯,下⾯内容区域的数据还是会保留,这个对于分析提交数据特别关键。
总结
我们在分析采集请求的时候,主要关⼼“⽹络”菜单⾥的请求数据,必要时候使⽤“保持”以查看刷新页⾯的请求数据,请求前可以使⽤“清除”先清除下⾯的内容。
案例解析
⼀、简单的采集
这⾥所指的简单采集,是指⼀个单⼀页⾯GET请求的采集,它简单得即使通过file_get_contents函数也能轻松获得页⾯返回结果。
代码⽚段之file_get_contents
<?php
$url = 'demo.zjmainstay/php/curl/simple.html';
$content = file_get_contents($url);
echo $content;
代码⽚段之cURL
<?php
$url = 'demo.zjmainstay/php/curl/simple.html';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
$content = curl_exec($ch); //执⾏并存储结果
curl_close($ch);
echo $content;
⼆、需要参数的采集
这种情况,页⾯请求需要传⼊⼀些参数,可以是GET请求,也可以是POST请求。这种情况的采集,使⽤file_get_contents外带⼀些参数还是可以实现的,但是这⾥我们将不再展⽰。
<?php
$keyword = 'PHP cURL';
$url = 'www.baidu/s?ie=utf-8&f=8&rsv_bp=1&ch=&tn=baidu&bar=&wd=' . urlencode($keyword);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
$content = curl_exec($ch); //执⾏并存储结果
curl_close($ch);
echo $content;
代码⽚段之cURL POST
对于POST类型的请求,我们平时并不少见,⽐如有些搜索就是使⽤POST⽅式提交,这时候我们就需要使⽤POST类型来提交参数了。这个在PHP cURL⾥⾯有相应的参数:CURLOPT_POST 和 CURLOPT_POSTFIELDS , CURLOPT_POST 的设置可以指定当前提交是否为POST⽅式,CURLOPT_POSTFIELDS则⽤于设定提交的参数,可以是参数串,也可以是参数数组,⽐如:
curl_setopt($ch, CURLOPT_POSTFIELDS, 'ie=utf-8&wd=PHP%20cURL');
或
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'ie' => 'utf-8',
'wd' => 'PHP%20cURL',
));
下⾯是我做的⼀个POST模拟搜索PHP POST 搜索,后端是使⽤了前⾯的百度关键词搜索,基本原理就是,客户端提交⼀个关键词到我服务器,我服务器使⽤该关键词请求百度的搜索,然后得到结果,返回到客户端。
如图(四)是利⽤Firebug对请求数据的分析,得到我们需要提交的请求链接和请求参数:
然后下⾯是我们的代码:
<?php
$keyword = 'PHP cURL';
//参数⽅法⼀
// $post = 'wd=' . urlencode($keyword);
//参数⽅法⼆
$post = array(
'wd' => urlencode($keyword),
);
$url = 'demo.zjmainstay/php/curl/search.php';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
curl_setopt($ch, CURLOPT_POST, 1); //发送POST类型数据
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST数据,$post可以是数组,也可以是拼接
$content = curl_exec($ch); //执⾏并存储结果
curl_close($ch);
var_dump($content);
三、需要Referer的采集
对于⼀些程序,它可能判断来源⽹址,如果发现referer不是⾃⼰的⽹站,则拒绝访问,这时候,我们就需要添加CURLOPT_REFERER参数,模拟来路,使得程序能够正常采集。
<?php
$keyword = 'PHP cURL';
//参数⽅法⼀
// $post = 'wd=' . urlencode($keyword);
//参数⽅法⼆
$post = array(
'wd' => urlencode($keyword),
);
$url = 'demo.zjmainstay/php/curl/search_refer.php';
$refer = 'demo.zjmainstay/'; //来路地址
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
curl_setopt($ch, CURLOPT_REFERER, $refer); //来路模拟
curl_setopt($ch, CURLOPT_POST, 1); //发送POST类型数据
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST数据,$post可以是数组,也可以是拼接
$content = curl_exec($ch); //执⾏并存储结果
curl_close($ch);
var_dump($content);
search_refer.php的源码如下,做了简单的Referer判断拦截:
<?php
if(empty($_POST['wd'])) {
exit('Deny empty params.');
}
//Referer判断
if(stripos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) === false) {
exit('Deny');
}
$keyword = addslashes(trim(strip_tags($_POST['wd'])));
$url = 'www.baidu/s?ie=utf-8&wd=' . urlencode($keyword);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
$content = curl_exec($ch); //执⾏并存储结果
curl_close($ch);
echo $content;
四、需要cookie⽀持的采集
对于模拟登录的应⽤,单单提交参数和模拟来路并不能解决问题,这时候我们就需要保存或者提交相应的Cookie参数,这个在PHP cURL⾥⾯也提供了相应的参数: CURLOPT_COOKIE:直接使⽤字符串⽅式提交cookie参数
CURLOPT_COOKIEFILE:使⽤⽂件⽅式提交cookie参数
CURLOPT_COOKIEJAR:保存提交后反馈的cookie数据
下⾯是PHP100的模拟登录⽰例:
<?php
header("content-Type: text/html; charset=UTF-8");
$cookie_file = tempnam('./temp', 'cookie');
$login_url="bbs.php100/login.php";
$post_fields="cktime=36000&step=2&pwuser=username&pwpwd=password";
//提交登录表单请求
$ch=curl_init($login_url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_POST,1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$post_fields);
curl_setopt($ch,CURLOPT_COOKIEJAR,$cookie_file); //存储提交后得到的cookie数据
curl_exec($ch);
curl_close($ch);
//登录成功后,获取bbs⾸页数据
$url="bbs.php100/index.php";
$ch=curl_init($url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_COOKIEFILE,$cookie_file); //使⽤提交后得到的cookie数据做参数
$contents=curl_exec($ch);
curl_close($ch);
//转码显⽰
echo iconv('gbk', 'UTF-8', $contents);
五、压缩⽹页采集(gzip)
有些没有接触过压缩页⾯的朋友估计会在这⾥被坑死,因为他们会发现采集回来的内容是乱码,并且⽆论使⽤iconv还是强⼤的mb_convert_encoding都⽆法还原数据,然后⼜没有概念,各种抓狂却不到⽅法,哈哈,我曾经也是这样~
如图(五)是乱码表现形式:
还好最后功夫不负有⼼⼈,还是到了,它就是CURLOPT_ENCODING参数。
⽐如,采集搜狐的新闻时候就遇到gzip压缩问题,下⾯是⽰例:
<?php
$url = 'news.sohu/';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip压缩
$content = curl_exec($ch); //执⾏并存储结果
curl_close($ch);
echo $content;
⼿册说明:⽀持的编码有"identity","deflate"和"gzip"。如果为空字符串"",请求头会发送所有⽀持的编码类型。
后⾯⼀句表明,使⽤curl_setopt($ch, CURLOPT_ENCODING, "");也是可以的,但是不能不加这个参数。
六、SSL链接的采集
有些请求链接是https类型的,这时候使⽤cURL采集可能会失败,这时候,我们可以使⽤ var_dump(c
url_error($ch));的⽅法打印错误提⽰,然后根据错误提⽰查相应的解决⽅案。⽐如SSL错误常见提⽰:SSL certificate problem: unable to get local issuer certificate,这时候,我们就需要利⽤参数:CURLOPT_SSL_VERIFYPEER 和
CURLOPT_SSL_VERIFYHOST 来禁⽤SSL证书的验证,我尝试过只使⽤CURLOPT_SSL_VERIFYPEER参数禁⽤失败,所以⼤家最好同时使⽤两个参数。
下⾯是代码⽰例:
<?php
$searchStr = 'RC376981638HK';
$post = 'accion=LocalizaUno&numero='.$searchStr.'&ecorreo=&numeros=';
$url = 's.es/localizadorenvios/track.asp';
$ch = curl_init($url); //初始化curl
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
curl_setopt($ch, CURLOPT_POST, 1); //发送POST类型数据
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST数据,$post可以是数组,也可以是拼接参数串
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //SSL 报错时使⽤
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //SSL 报错时使⽤
$contents = curl_exec($ch); //执⾏并存储结果
// var_dump(curl_error($ch)); //获取失败是使⽤(采集错误提⽰)
curl_close($ch);
echo $contents;
七、代理采集
⼤家都知道,国内存在万恶的墙,所以,假如我们需要获取某些被墙数据时,就需要⽤到国外代理服务器;⼜或者我们需要采集⼤量数据时,需要不断切换IP,也会⽤到代理。使⽤代理在PHP cURL⾥
⾯有⼏个相对应的参数:CURLOPT_PROXY、CURLOPT_PROXYPORT 和 CURLOPT_PROXYUSERPWD,还有另外⼏个,这⾥不列举。CURLOPT_PROXY 指定代理IP参数
CURLOPT_PROXYPORT 指定代理端⼝参数
CURLOPT_PROXYUSERPWD 指定需要验证的代理的账号密码,"[username]:[password]"格式的字符串
关于代理账号获取,⼤家⾃⼰发挥,我这⾥提供⽹上搜索到的⼀个列表:cURL ⾼匿代理
下⾯是代理采集⽰例:
<?php
$url = 'demo.zjmainstay/php/curl/dump_ip.php?t=' . time();
echo "本地IP:" . file_get_contents($url) . "\n伪造IP:";
$ip = '183.224.1.116';
$port = '80';
//伪造请求头参数,如果是⾼匿代理这⾥不需要提供
$header = array(
'X-FORWARDED-FOR: ' . $ip,
'CLIENT-IP: ' . $ip,
);
$ch = curl_init($url); //初始化curl
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_PROXY, $ip);
curl_setopt($ch, CURLOPT_PROXYPORT, $port);
$content = curl_exec($ch); //执⾏并存储结果
curl_close($ch);
echo $content;
⼋、多线程采集
对于⼤量采集⼯作,为了提⾼采集效率,使⽤PHP cURL提供的多线程采集是必不可少的。⼿册上提供的多线程采集例⼦好像都不太好⽤,我刚开始也从⾥⾯测试了⼏个例⼦,但是发现都是执⾏卡死,根本⽆法执⾏完成,前⼏天突然⼜测试了⼀下,然后发现curl_multi_info_read函数下⾯的Example #1是可以执⾏的,它的内容在$res上,但是没有打印出来,⽽且雅虎的请求⽐较慢,会卡住,前⾯两个链接都能正常返回。
不过,还好当时的例⼦不好⽤,然后我经过搜索到了⼀个很厉害的项⽬,CurlMulti ,它对PHP cURL Multi 进⾏了⼀个良性扩展的封装,能够很好地提供采集⽀持。
关于CurlMulti的使⽤我就不多介绍,官⽹上⾯提供了demo,使⽤过程有技术难题可以直接加⼊Q讨论,作者@Ares 和其他的采集⼤⽜都会提供技术解答帮助。
下⾯是PHP cURL Multi的⼀个简单⽰例:
<?php
$urls = array(
"demo.zjmainstay/php/curl/curl_multi_1.php",
"demo.zjmainstay/php/curl/curl_multi_2.php",
);
curl命令发送post请求带参数$mh = curl_multi_init();
foreach ($urls as $i => $url) {
$conn[$i] = curl_init($url);
curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1); //不直接输出结果
curl_multi_add_handle($mh, $conn[$i]);
}
$active = null;
$res = array();
do {
$status = curl_multi_exec($mh, $active);
$info = curl_multi_info_read($mh);
if (false !== $info) {
//采集信息处理
$res[] = array(
'content' => curl_multi_getcontent($info['handle']),
'info' => $info,
);
curl_close($info['handle']);
}
} while ($status === CURLM_CALL_MULTI_PERFORM || $active);
curl_multi_close($mh);
var_dump($res);
九、302跳转(301跳转)
对于⼀些应⽤,⽐如模拟登录,如果遇上302跳转,会导致cookie丢失⽽使得模拟登录失败,请求现象如图(六)所⽰:
这个时候,可以使⽤:
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
关于CURLOPT_FOLLOWLOCATION,⼿册说明是:
启⽤时会将服务器服务器返回的"Location: "放在header中递归的返回给服务器,使⽤CURLOPT_MAXREDIRS可以限定递归返回的数量。
我个⼈理解,通俗点讲就是后⾯的跳转会继续跟踪访问,⽽且cookie在header⾥⾯被保留了下来。
⼗、模拟上传⽂件
在PHP⼿册的curl_setopt函数中,关于CURLOPT_POSTFIELDS有如下描述:
全部数据使⽤HTTP协议中的"POST"操作来发送。要发送⽂件,在⽂件名前⾯加上@前缀并使⽤完整路径。这个参数可以通过urlencoded后的字符串类似'para1=val1¶2=val2&...'或使⽤⼀个以字段名为键值,字段数据为值的数组。如果value是⼀个数对于上传⽂件,这句话包含两个信息:
1. 要上传⽂件,post的数据参数必须使⽤数组,使得Content-Type头将会被设置成multipart/form-data。
2. 要上传⽂件,在⽂件名前⾯加上@前缀并使⽤完整路径。
因此,模拟⽂件上传可以按照如下实现:
//上传D盘下的test.jpg⽂件,⽂件必须存在,否则curl处理失败且没有任何提⽰
$data = array('name' => 'Foo', 'file' => '@d:/test.jpg');
$ch = curl_init('localhost/upload.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_exec($ch);
本地测试的时候,在upload.php⽂件中打印出\\(_POST和\$_FILES即可验证是否上传成功,如下: ``` <?php print_r(\)_POST);
print_r($_FILES);
输出结果类似:
Array ( [name] => Foo ) Array ( [file] => Array ( [name] => test.jpg [type] => application/octet-stream [tmp_name] => D:\xampp\p [error] => 0 [size] => 139999 ) )
关于CURLOPT_POSTFIELDS的赋值,另外补充⼀句描述:
传递⼀个数组到CURLOPT_POSTFIELDS,cURL会把数据编码成 multipart/form-data,⽽然传递⼀
个URL-encoded字符串时,数据会被编码成 application/x-www-form-
urlencoded。
即:
curl_setopt(\(ch, CURLOPT_POSTFIELDS, 'param1=val1¶m2=val2&...'); 和 curl_setopt(\)ch, CURLOPT_POSTFIELDS, array('param1' => 'val1', 'param2' => 'val2', ...));
这样⼀个功能强⼤的采集神器cURL的使⽤⽅法为⼤家介绍到这,希望对⼤家的学习有所帮助。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论