php curl

PHP cURL实现模拟登陆与采集使用方法详解教程Featured

做者:  Zjmainstay

本文将经过案例,整合浏览器工具与PHP程序,教你如何让数据 唾手可得 。javascript

对于作过数据采集的人来讲,cURL必定不会陌生。虽然在PHP中有file_get_contents函数能够获取远程连接的数据,可是它的可控制性太差了,对于各类复杂状况的采集情景,file_get_contents显得有点无能为力。所以,本文将为你介绍采集神器cURL的使用。php

 

 

 

工具

 

火狐浏览器(FireFox) + Firebug

“工欲善其事,必先利其器。” 在分析案例以前,先让咱们学习一下如何利用神器Firebug获取咱们必要的信息。 
使用F12打开Firebug,咱们能够获得如图(一)界面: 
Firebughtml

  1. 箭头图标是“元素选择”工具,单击一次会高亮图标,同时,鼠标在页面内的移动会同时在HTML菜单中选定相应的内容,此时单击内容则表示选定了该元素,图标高亮取消。如图(二)所示: 
    Firebug查看元素java

  2. 控制台 
    JS里面的console.log系列函数的打印就是在这里输出。ios

  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数据等。在页面提交会刷新的状况下,须要使用保持,使得页面请求内容在刷新后仍然留着控制台中,如图(三)所示: 
    Firebug网络保持
    另外,火狐还有一款 Tamper data 扩展也能获得请求数据,必要时能够安装使用。
  8. Cookies 
    Cookie数据

在图(一)中还看到下面有不少可选的小菜单项,其中保持是咱们要关注的,当选择它的时候,即便提交表单刷新了页面,下面内容区域的数据仍是会保留,这个对于分析提交数据特别关键。git

 

总结

咱们在分析采集请求的时候,主要关心“网络”菜单里的请求数据,必要时候使用“保持”以查看刷新页面的请求数据,请求前可使用“清除”先清除下面的内容。github

 

案例解析

 

1、简单的采集

这里所指的简单采集,是指一个单一页面GET请求的采集,它简单得即便经过file_get_contents函数也能轻松得到页面返回结果。web


  • 代码片断之file_get_contents
 
  1. <?php
  2. $url = 'http://demo.zjmainstay.cn/php/curl/simple.html';
  3. $content = file_get_contents($url);
  4. echo $content;

  • 代码片断之cURL
 
  1. <?php
  2. $url = 'http://demo.zjmainstay.cn/php/curl/simple.html';
  3. $ch = curl_init($url);
  4. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
  5. $content = curl_exec($ch); //执行并存储结果
  6. curl_close($ch);
  7. echo $content;

 

2、须要参数的采集

这种状况,页面请求须要传入一些参数,能够是GET请求,也能够是POST请求。这种状况的采集,使用file_get_contents外带一些参数仍是能够实现的,可是这里咱们将再也不展现。ajax

  • 代码片断之cURL GET 
    这种请求,咱们能够选择搜索引擎做为演示,好比我百度搜索一个词语“PHP cURL”,在输入回车后,咱们会获得一个相似http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&ch=&tn=baidu&bar=&wd=PHP%20cURL的连接,注意这里的连接可能不一样浏览器、不一样入口方式访问获得不同结果,所以没必要介意连接是否同样。经过输入多个关键词并观察连接,咱们能够肯定 wd 参数就是咱们要传入的动态参数,而其余参数则能够不变,所以获得咱们下面的采集代码。
 
  1. <?php
  2. $keyword = 'PHP cURL';
  3. $url = 'http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&ch=&tn=baidu&bar=&wd=' . urlencode($keyword);
  4. $ch = curl_init($url);
  5. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
  6. $content = curl_exec($ch); //执行并存储结果
  7. curl_close($ch);
  8. echo $content;

有些时候,一些参数并非必须的,这时候咱们能够删掉它,好比上面的连接能够只保留http://www.baidu.com/s?ie=utf-8&wd=PHP%20cURL,ie=utf-8 这个参数可能影响结果的编码,因此暂且留着它。就这样简单的代码,咱们就能够采集到百度搜索的结果了。json

  • 代码片断之cURL POST 
    对于POST类型的请求,咱们平时并很多见,好比有些搜索就是使用POST方式提交,这时候咱们就须要使用POST类型来提交参数了。这个在PHP cURL里面有相应的参数:CURLOPT_POST 和 CURLOPT_POSTFIELDS , CURLOPT_POST 的设置能够指定当前提交是否为POST方式,CURLOPT_POSTFIELDS则用于设定提交的参数,能够是参数串,也能够是参数数组,好比:
 
  1. curl_setopt($ch, CURLOPT_POSTFIELDS, 'ie=utf-8&wd=PHP%20cURL');
  2. curl_setopt($ch, CURLOPT_POSTFIELDS, array(
  3. 'ie' => 'utf-8',
  4. 'wd' => 'PHP%20cURL',
  5. ));

下面是我作的一个POST模拟搜索PHP POST 搜索,后端是使用了前面的百度关键词搜索,基本原理就是,客户端提交一个关键词到我服务器,我服务器使用该关键词请求百度的搜索,而后获得结果,返回到客户端。 
如图(四)是利用Firebug对请求数据的分析,获得咱们须要提交的请求连接和请求参数: 
cURL POST 参数分析

而后下面是咱们的代码:

 
  1. <?php
  2. $keyword = 'PHP cURL';
  3. //参数方法一
  4. // $post = 'wd=' . urlencode($keyword);
  5. //参数方法二
  6. $post = array(
  7. 'wd' => urlencode($keyword),
  8. );
  9. $url = 'http://demo.zjmainstay.cn/php/curl/search.php';
  10. $ch = curl_init($url);
  11. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
  12. curl_setopt($ch, CURLOPT_POST, 1); //发送POST类型数据
  13. curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST数据,$post能够是数组,也能够是拼接
  14. $content = curl_exec($ch); //执行并存储结果
  15. curl_close($ch);
  16. var_dump($content);

 

3、须要Referer的采集

对于一些程序,它可能判断来源网址,若是发现referer不是本身的网站,则拒绝访问,这时候,咱们就须要添加CURLOPT_REFERER参数,模拟来路,使得程序可以正常采集。

 
  1. <?php
  2. $keyword = 'PHP cURL';
  3. //参数方法一
  4. // $post = 'wd=' . urlencode($keyword);
  5. //参数方法二
  6. $post = array(
  7. 'wd' => urlencode($keyword),
  8. );
  9. $url = 'http://demo.zjmainstay.cn/php/curl/search_refer.php';
  10. $refer = 'http://demo.zjmainstay.cn/'; //来路地址
  11. $ch = curl_init($url);
  12. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
  13. curl_setopt($ch, CURLOPT_REFERER, $refer); //来路模拟
  14. curl_setopt($ch, CURLOPT_POST, 1); //发送POST类型数据
  15. curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST数据,$post能够是数组,也能够是拼接
  16. $content = curl_exec($ch); //执行并存储结果
  17. curl_close($ch);
  18. var_dump($content);

search_refer.php的源码以下,作了简单的Referer判断拦截:

 
  1. <?php
  2. if(empty($_POST['wd'])) {
  3. exit('Deny empty params.');
  4. }
  5. //Referer判断
  6. if(stripos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) === false) {
  7. exit('Deny');
  8. }
  9. $keyword = addslashes(trim(strip_tags($_POST['wd'])));
  10. $url = 'http://www.baidu.com/s?ie=utf-8&wd=' . urlencode($keyword);
  11. $ch = curl_init($url);
  12. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
  13. $content = curl_exec($ch); //执行并存储结果
  14. curl_close($ch);
  15. echo $content;

 

4、须要cookie支持的采集

对于模拟登陆的应用,单单提交参数和模拟来路并不能解决问题,这时候咱们就须要保存或者提交相应的Cookie参数,这个在PHP cURL里面也提供了相应的参数: 
CURLOPT_COOKIE: 直接使用字符串方式提交cookie参数 
CURLOPT_COOKIEFILE: 使用文件方式提交cookie参数 
CURLOPT_COOKIEJAR: 保存提交后反馈的cookie数据

 

(一)模拟登陆示例

下面是PHP100的模拟登陆示例:

 
  1. <?php
  2. header("content-Type: text/html; charset=UTF-8");
  3. $cookie_file = tempnam('./temp', 'cookie');
  4. $login_url="http://bbs.php100.com/login.php";
  5. $post_fields="cktime=36000&step=2&pwuser=username&pwpwd=password";
  6. //提交登陆表单请求
  7. $ch=curl_init($login_url);
  8. curl_setopt($ch,CURLOPT_HEADER,0);
  9. curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
  10. curl_setopt($ch,CURLOPT_POST,1);
  11. curl_setopt($ch,CURLOPT_POSTFIELDS,$post_fields);
  12. curl_setopt($ch,CURLOPT_COOKIEJAR,$cookie_file); //存储提交后获得的cookie数据
  13. curl_exec($ch);
  14. curl_close($ch);
  15. //登陆成功后,获取bbs首页数据
  16. $url="http://bbs.php100.com/index.php";
  17. $ch=curl_init($url);
  18. curl_setopt($ch,CURLOPT_HEADER,0);
  19. curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
  20. curl_setopt($ch,CURLOPT_COOKIEFILE,$cookie_file); //使用提交后获得的cookie数据作参数
  21. $contents=curl_exec($ch);
  22. curl_close($ch);
  23. //转码显示
  24. echo iconv('gbk', 'UTF-8', $contents);
 

(二)自动模拟登陆实现

最近研究出来一个自动模拟登陆的类,请查看《PHP基于cURL实现自动模拟登陆》了解。(补充于2016.7.31)

 

5、压缩网页采集(gzip)

有些没有接触过压缩页面的朋友估计会在这里被坑死,由于他们会发现采集回来的内容是乱码,而且不管使用iconv仍是强大的mb_convert_encoding都没法还原数据,而后又没有概念,各类抓狂却找不到方法,哈哈,我曾经也是这样~ 
如图(五)是乱码表现形式: 
cURL 乱码 解决
还好最后功夫不负有心人,仍是找到了,它就是CURLOPT_ENCODING参数。 
好比,采集搜狐的新闻时候就遇到gzip压缩问题,下面是示例:

 
  1. <?php
  2. $url = 'http://news.sohu.com/';
  3. $ch = curl_init($url);
  4. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
  5. curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip压缩
  6. $content = curl_exec($ch); //执行并存储结果
  7. curl_close($ch);
  8. echo $content;

手册说明:支持的编码有"identity","deflate"和"gzip"。若是为空字符串"",请求头会发送全部支持的编码类型。 
后面一句代表,使用curl_setopt($ch, CURLOPT_ENCODING, "");也是能够的,可是不能不加这个参数。

 

6、SSL连接的采集

有些请求连接是https类型的,这时候使用cURL采集可能会失败,这时候,咱们可使用 var_dump(curl_error($ch));的方法打印错误提示,而后根据错误提示查找相应的解决方案。好比SSL错误常见提示:SSL certificate problem: unable to get local issuer certificate,这时候,咱们就须要利用参数:CURLOPT_SSL_VERIFYPEER 和 CURLOPT_SSL_VERIFYHOST 来禁用SSL证书的验证,我尝试过只使用CURLOPT_SSL_VERIFYPEER参数禁用失败,因此你们最好同时使用两个参数。 
下面是代码示例:

 
  1. <?php
  2. $searchStr = 'RC376981638HK';
  3. $post = 'accion=LocalizaUno&numero='.$searchStr.'&ecorreo=&numeros=';
  4. $url = 'https://aplicacionesweb.correos.es/localizadorenvios/track.asp';
  5. $ch = curl_init($url); //初始化curl
  6. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
  7. curl_setopt($ch, CURLOPT_POST, 1); //发送POST类型数据
  8. curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST数据,$post能够是数组,也能够是拼接参数串
  9. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //SSL 报错时使用
  10. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //SSL 报错时使用
  11. $contents = curl_exec($ch); //执行并存储结果
  12. // var_dump(curl_error($ch)); //获取失败是使用(采集错误提示)
  13. curl_close($ch);
  14. echo $contents;

今天(2016.8.20)遇到一个错误error:14077458:SSL routines:SSL23_GET_SERVER_HELLO:reason(1112),测试追加参数

 
  1. //值有0-6,请参考手册,值1不行试试其余值
  2. curl_setopt($ch, CURLOPT_SSLVERSION, 1);

可解决问题。

 

7、代理采集

你们都知道,国内存在万恶的墙,因此,假如咱们须要获取某些被墙数据时,就须要用到国外代理服务器;又或者咱们须要采集大量数据时,须要不断切换IP,也会用到代理。 
使用代理在PHP cURL里面有几个相对应的参数:CURLOPT_PROXY、CURLOPT_PROXYPORT 和 CURLOPT_PROXYUSERPWD,还有另外几个,这里不列举。 
CURLOPT_PROXY 指定代理IP参数 
CURLOPT_PROXYPORT 指定代理端口参数 
CURLOPT_PROXYUSERPWD 指定须要验证的代理的帐号密码,"[username]:[password]"格式的字符串

关于代理帐号获取,你们本身发挥,我这里提供网上搜索到的一个列表:cURL 高匿代理

下面是代理采集示例:

 
  1. <?php
  2. $url = 'http://demo.zjmainstay.cn/php/curl/dump_ip.php?t=' . time();
  3. echo "本地IP:" . file_get_contents($url) . "\n伪造IP:";
  4. $ip = '183.224.1.116';
  5. $port = '80';
  6. //伪造请求头参数,若是是高匿代理这里不须要提供
  7. $header = array(
  8. 'X-FORWARDED-FOR: ' . $ip,
  9. 'CLIENT-IP: ' . $ip,
  10. );
  11. $ch = curl_init($url); //初始化curl
  12. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  13. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  14. curl_setopt($ch, CURLOPT_PROXY, $ip);
  15. curl_setopt($ch, CURLOPT_PROXYPORT, $port);
  16. $content = curl_exec($ch); //执行并存储结果
  17. curl_close($ch);
  18. echo $content;
 

8、 多线程采集

对于大量采集工做,为了提升采集效率,使用PHP cURL提供的多线程采集是必不可少的。手册上提供的多线程采集例子好像都不太好用,我刚开始也从里面测试了几个例子,可是发现都是执行卡死,根本没法执行完成,前几天忽然又测试了一下,而后发现curl_multi_info_read函数下面的Example #1是能够执行的,它的内容在$res上,可是没有打印出来,并且雅虎的请求比较慢,会卡住,前面两个连接都能正常返回。 
不过,还好当时的例子很差用,而后我通过搜索找到了一个很厉害的项目,CurlMulti ,它对PHP cURL Multi 进行了一个良性扩展的封装,可以很好地提供采集支持。 
关于CurlMulti的使用我就很少介绍,官网上面提供了demo,使用过程有技术难题能够直接加入Q群( cURL高级技术 215348766 )讨论,做者@Ares 和其余的采集大牛都会提供技术解答帮助。 
下面是PHP cURL Multi的一个简单示例:

 
  1. <?php
  2. $urls = array(
  3. array(
  4. 'url' => 'http://demo.zjmainstay.cn/php/curl/curl_multi_1.php',
  5. 'id' => 1,
  6. ),
  7. array(
  8. 'url' => 'http://demo.zjmainstay.cn/php/curl/curl_multi_2.php',
  9. 'id' => 2,
  10. ),
  11. );
  12. $mh = curl_multi_init();
  13. $conn = array();
  14. foreach ($urls as $urlItem) {
  15. $ch = curl_init($urlItem['url']);
  16. $conn[(int)$ch] = $urlItem; //记录资源与参数映射
  17. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //不直接输出结果
  18. curl_multi_add_handle($mh, $ch);
  19. }
  20. $active = null;
  21. $res = array();
  22. do {
  23. $status = curl_multi_exec($mh, $active);
  24. $info = curl_multi_info_read($mh);
  25. if (false !== $info) {
  26. //采集信息处理
  27. $res[] = array(
  28. 'content' => curl_multi_getcontent($info['handle']),
  29. 'info' => $info,
  30. 'param' => $conn[(int)$info['handle']],
  31. );
  32. curl_close($info['handle']);
  33. }
  34. } while ($status === CURLM_CALL_MULTI_PERFORM || $active);
  35. curl_multi_close($mh);
  36. var_dump($res);

附:全站克隆本地化网页爬虫示例

 

9、302跳转(301跳转)

对于一些应用,好比模拟登陆,若是赶上302跳转,会致使cookie丢失而使得模拟登陆失败,请求现象如图(六)所示:

curl 302跳转

这个时候,可使用:

 
  1. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

关于CURLOPT_FOLLOWLOCATION,手册说明是:

启用时会将服务器服务器返回的"Location: "放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS能够限定递归返回的数量。

我我的理解,通俗点讲就是后面的跳转会继续跟踪访问,并且cookie在header里面被保留了下来。

 

10、模拟上传文件

(一)基于本地文件上传

在PHP手册的curl_setopt函数中,关于CURLOPT_POSTFIELDS有以下描述:

 
  1. 所有数据使用HTTP协议中的"POST"操做来发送。要发送文件,在文件名前面加上@前缀并使用完整路径。这个参数能够经过urlencoded后的字符串相似'para1=val1&para2=val2&...'或使用一个以字段名为键值,字段数据为值的数组。若是value是一个数组,Content-Type头将会被设置成multipart/form-data

对于上传文件,这句话包含两个信息: 
1. 要上传文件,post的数据参数必须使用数组,使得Content-Type头将会被设置成multipart/form-data。 
2. 要上传文件,在文件名前面加上@前缀并使用完整路径。 
注意:PHP 5.5.0起,文件上传建议使用CURLFile代替@

所以,模拟文件上传能够按照以下实现:

 
(1)单文件上传
 
  1. //上传D盘下的test.jpg文件,文件必须存在,不然curl处理失败且没有任何提示,Windows下中文文件名上传失败时,可以使用iconv('UTF-8', 'GBK//IGNORE', $filename)转码下文件名再上传。
  2. $data = array('name' => 'Foo', 'file' => '@d:/test.jpg');
  3. 注: PHP 5.5.0起,文件上传建议使用CURLFile代替@
  4. $ch = curl_init('http://localhost/upload.php');
  5. curl_setopt($ch, CURLOPT_POST, 1);
  6. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  7. curl_exec($ch);

本地测试的时候,在upload.php文件中打印出$_POST和$_FILES便可验证是否上传成功,以下:

 
  1. <?php
  2. print_r($_POST);
  3. print_r($_FILES);

输出结果相似:

 
  1. Array ( [name] => Foo ) Array ( [file] => Array ( [name] => test.jpg [type] => application/octet-stream [tmp_name] => D:\xampp\tmp\php2EA0.tmp [error] => 0 [size] => 139999 ) )
 
(2)多文件上传
 
  1. <?php
  2. // 注: PHP 5.5.0起,文件上传建议使用CURLFile代替@
  3. // 多文件上传
  4. $data = array(
  5. 'input_file[0]' => new CURLFile('d:/1.txt', 'text/plain', 'testfile.txt'),
  6. 'input_file[1]' => new CURLFile('d:/2.txt', 'text/plain'),
  7. 'input_file[2]' => new CURLFile('d:/3.txt', 'text/plain'),
  8. );
  9. $ch = curl_init('http://demo.zjmainstay.cn/php/curl/curlUploadHandler.php');
  10. curl_setopt($ch, CURLOPT_POST, 1);
  11. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  12. curl_exec($ch);

输出结果相似:

 
  1. Array ( [upload_file] => Array ( [name] => Array ( [0] => 1.txt [1] => 2.txt [2] => 3.txt ) [type] => Array ( [0] => text/plain [1] => text/plain [2] => text/plain ) [tmp_name] => Array ( [0] => /tmp/phpkQ75hZ [1] => /tmp/phpuVB5La [2] => /tmp/phpu1z5fm ) [error] => Array ( [0] => 0 [1] => 0 [2] => 0 ) [size] => Array ( [0] => 2 [1] => 2 [2] => 2 ) ) )
 
(3)CURLOPT_POSTFIELDS使用字符串与数组的区别

关于CURLOPT_POSTFIELDS的赋值,另外补充一句描述:

 
  1. 传递一个数组到CURLOPT_POSTFIELDScURL会把数据编码成 multipart/form-data,而然传递一个URL-encoded字符串时,数据会被编码成 application/x-www-form-urlencoded

即:

 
  1. curl_setopt($ch, CURLOPT_POSTFIELDS, 'param1=val1&param2=val2&...');
  2. curl_setopt($ch, CURLOPT_POSTFIELDS, array('param1' => 'val1', 'param2' => 'val2', ...));

至关于html的form表单中:

 
  1. <form method="post" action="upload.php">

 
  1. <form method="post" action="upload.php" enctype="multipart/form-data">

的区别。

 

(二)基于采集文件内容上传

对于数据采集回来的文件内容,有时候须要再次上传到其余的文件服务器上,此时,若是本地先存储再利用上面的方式提交到文件服务器,显然会多了一次IO写入和读取操做,对于这种状况,咱们能够利用构造模拟上传请求头和构造数据报文,直接利用采集回来的文件内容上传到文件服务器:

 
  1. <?php
  2. /**
  3. * @author 若是的若是
  4. * PHP利用cURL直接以文件内容形式上传文件
  5. * @param string $url 文件上传处理连接
  6. * @param array $fileFields 文件上传数据数组
  7. * @param array $postFields 非文件表单数据数组
  8. * @param array $curlOpt 扩展的CURLOPT_数据
  9. * @return string
  10. */
  11. function curlPostMemoryFile($url, $fileFields, $postFields = array(), $curlOpt = array()){
  12. //构造post数据
  13. $data = '';
  14. $delimiter = '-------------' . uniqid();
  15. // 表单数据
  16. foreach ($postFields as $name => $content) {
  17. $data .= "--" . $delimiter . "\r\n";
  18. $data .= 'Content-Disposition: form-data; name="' . $name . '"';
  19. $data .= "\r\n\r\n";
  20. $data .= $content;
  21. $data .= "\r\n";
  22. //$data .= "--" . $delimiter . "\r\n";
  23. }
  24. // 文件上传数据
  25. foreach ($fileFields as $inputName => $file) {
  26. $data .= "--" . $delimiter . "\r\n";
  27. $data .= 'Content-Disposition: form-data; name="' . $inputName . '";' .
  28. ' filename="' . $file['filename'] . '"' . "\r\n";
  29. $data .= 'Content-Type: ' . $file['type'] . "\r\n";
  30. $data .= "\r\n";
  31. $data .= $file['content'] . "\r\n";
  32. }
  33. $data .= "--" . $delimiter . "--\r\n";
  34. //post请求提交文件上传数据
  35. $handle = curl_init($url);
  36. curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1); //返回数据不直接输出
  37. $header = array(
  38. 'Content-Type: multipart/form-data; boundary=' . $delimiter,
  39. 'Content-Length: ' . strlen($data)
  40. );
  41. if(isset($curlOpt[CURLOPT_HTTPHEADER])) {
  42. $header = array_merge($header, $curlOpt[CURLOPT_HTTPHEADER]);
  43. unset($curlOpt[CURLOPT_HTTPHEADER]);
  44. }
  45. curl_setopt($handle, CURLOPT_HTTPHEADER , $header);
  46. curl_setopt($handle, CURLOPT_POST, true);
  47. curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
  48. if(!empty($curlOpt)){
  49. foreach($curlOpt as $key => $val){
  50. curl_setopt($handle,$key,$val);
  51. }
  52. }
  53. return curl_exec($handle);
  54. }
  55. //演示
  56. $url = 'http://demo.zjmainstay.cn/php/curl/curlUploadHandler.php';
  57. $filename = 'test.txt';
  58. $fileContent = '测试直接文件内容形式上传文件';
  59. $fileFields = array(
  60. 'upload_file' => array(
  61. 'filename' => $filename,
  62. #从正常上传时的post数据中查看,对应$_FILES里的type
  63. 'type' => 'text/plain',
  64. 'content' => $fileContent,
  65. ),
  66. );
  67. $postFields = array(
  68. 'name' => 'Zjmainstay',
  69. 'age' => '26',
  70. );
  71. //此处测试CURLOPT_HTTPHEADER的合并
  72. $header = array(
  73. 'Host: demo.zjmainstay.cn' ,
  74. );
  75. $curlOpt = array(
  76. CURLOPT_HTTPHEADER => $header,
  77. );
  78. $content = curlPostMemoryFile($url, $fileFields, $postFields, $curlOpt);
  79. var_dump($content);
 

11、发送与获取json数据

发送json数据,在控制台中的表现主要如图(七)所示: 
ajax发送json的控制台信息
第一条发送的是json格式的数据, 
第二条发送的是以\n分割的数据, 
第三条发送的是以&分割的数据。 
这个在ajax请求的时候,只须要添加contentType参数便可,如:

 
  1. var data = ["name:Zjmainstay", "website:http://www.zjmainstay.cn"];
  2. $.ajax({
  3. url: 'http://test.com/curl/testPostJsonData.php',
  4. type: 'post',
  5. data: data.join("\n"),
  6. contentType: 'text/plain',
  7. success: function(result) {
  8. console.log(result);
  9. },
  10. error: function(msg) {
  11. console.log(msg);
  12. }
  13. });

对于这类发送json数据的请求,复制cURL命令时,你会发现其中根本没有发送数据,如:

 
  1. curl 'http://test.com/curl/testPostJsonData.php' -X POST -H 'Accept: */*' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3' -H 'Cache-Control: max-age=0' -H 'Connection: keep-alive' -H 'Content-Length: 48' -H 'Content-Type: text/plain' -H 'Cookie: __utma=99889051.942646074.1467634856.1467634856.1467636947.2' -H 'Host: test.com' -H 'Referer: http://test.com/curl/ajaxJsonData.html' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:47.0) Gecko/20100101 Firefox/47.0' -H 'X-Requested-With: XMLHttpRequest'

对于这种数据,咱们要用PHP cURL模拟发送的话,须要发送相应的header参数,示例:

 
  1. <?php
  2. #json数据
  3. $url = 'http://test.com/curl/testPostJsonData.php';
  4. $data = '{"a":"b"}';
  5. $length = strlen($data);
  6. $header = array(
  7. 'Content-Length: ' . $length, //不是必需的
  8. 'Content-Type: text/json',
  9. );
  10. $ch = curl_init($url); //初始化curl
  11. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  12. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  13. curl_setopt($ch, CURLOPT_POST, 1);
  14. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  15. $content = curl_exec($ch); //执行并存储结果
  16. curl_close($ch);
  17. echo $content;
  18. #\n分割数据
  19. $data = [
  20. 'name:Zjmainstay',
  21. 'website:http://www.zjmainstay.cn',
  22. ];
  23. $data = implode("\n", $data);
  24. #&分割数据
  25. $data = 'name:Zjmainstay&website:http://www.zjmainstay.cn';

对于$.ajax咱们经常会使用这种写法来指定返回json格式数据:

 
  1. $.ajax({
  2. url: url,
  3. dataType: 'json',
  4. ...
  5. });

咱们会发现,cURL请求头Accept:部分会多出一些参数:

 
  1. -H 'Accept: application/json, text/javascript, */*; q=0.01'

所以,若是须要指定返回内容做为json格式,咱们就须要指定application/json格式。

那么,对于这种给服务器端发送json数据的程序,服务器端是怎么获得请求数据的呢? 
若是大家作过尝试,必定会发现此时$_POST是空的,咱们要使用php://input进行数据获取,示例:

 
  1. $postData = file_get_contents('php://input');
 

12、POST提交大数据(超过1024字节)异常解决方法

在使用cURL作POST的时候,当要POST的数据大于1024字节的时候,cURL并不会直接就发起POST请求, 而是会分为俩步:

 
  1. 1. 发送一个请求, 包含一个Expect:100-continue, 询问Server是否愿意接受数据
  2. 2. 接收到Server返回的100-continue应答之后, 才把数据POSTServer

解决:

 
  1. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
 

十3、获取请求头部和丢弃BODY内容

有时候,被采集的站点把下一次跳转的连接放入了响应头里,这时候,咱们须要经过设置CURLOPT_HEADER属性为true,让响应结果携带响应头便可。

 
  1. curl_setopt($ch, CURLOPT_HEADER, true);

若是咱们只须要获取响应头,或者有时候只是为了测试某个连接是否可以正常请求,咱们能够经过设置CURLOPT_NOBODYtrue,让响应结果不返回BODY内容。

 
  1. curl_setopt($ch, CURLOPT_NOBODY, true);
 

总结

 

通用curl页面采集函数

PHP通用cURL页面采集函数

 

简易解析cURL命令获得PHP代码程序

简易解析cURL命令获得PHP代码程序

PHP cURL是一个很强大的采集工具,curl_setopt里面还有不少参数,读者能够抽空总体看一遍,虽然平时未必用得上,可是至少作到内心有底,知道都有哪些参数,必要时还能找出来使用。 采集是一项大工程,使用过程当中遇到的问题还会很多,可是只要学会分析和资料搜索,一切都会迎刃而解的,你们加油!哈哈~~

相关文章
相关标签/搜索