转的文章。主要是因为经常会用curl来抓取数据。而且几乎是http_get和http_post混用。但我没遇上这个BUG。不清楚以后会不会遇上,所以我先记录一下,以防万一遇到时候不知道怎么做。
原文地址来自:http://www.ideawu.net/blog/archives/622.html
重用一个CURL句柄时, 发现curl_setopt($ch, CURLOPT_HTTPGET, TRUE) 不起作用. 期望在调用这条语句之后发起请求, 应该发送的是GET, 但看服务器log, 却使用了和前一次请求相同的HTTP方法.
PHP脚本:
<?php $url = 'http://www.ideawu.net/'; $ch = curl_init($url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_exec($ch); curl_setopt($ch, CURLOPT_HTTPGET, true); // 错误! BUG curl_exec($ch); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); // 正确 curl_exec($ch); ?>
web server log:
124.127.130.50 "2011-10-12 18:55:09" "POST / HTTP/1.1" 200 3516 "-" www.ideawu.net 124.127.130.50 "2011-10-12 18:55:09" "POST / HTTP/1.1" 200 3516 "-" www.ideawu.net 124.127.130.50 "2011-10-12 18:55:09" "GET / HTTP/1.1" 200 3516 "-" www.ideawu.net
这个BUG目前还没找到相关的资料.
补充: 不仅仅是CURLOPT_HTTPGET, CURLOPT_POST也有同样的问题. 所以, 结论是: 只有CURLOPT_CUSTOMREQUEST才是正确的方法.
开发中又遇到一些事情,于是记录一下
1、CURL的问题
curl在安全模式下或者设定了open_basedir的情况下,如果使用了OPT_FOLLOWLOCATION,会导致无返回值。这个理由很多,但FOLLOWLOCATION这个参数是用于目标网址会多次跳转而使用,还可以设置最大跳转次数,因此,如果你要抓取的对象有多次跳转,这个参数就非设不可(真纠结,实在不行就file_get_contents了,它自动支持多次跳转,但不如curl更可控一些)
具体关于CURL的一些常用参数,可以看这里:
http://opensuse.iteye.com/blog/349829
- curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
-
- curl_setopt($ch, CURLOPT_NOPROGRESS, 1);
-
- curl_setopt($ch, CURLOPT_NOBODY, 0);
-
- curl_setopt($ch, CURLOPT_HTTPGET, 1);
-
- curl_setopt($ch, CURLOPT_ENCODING, ”);
-
- curl_setopt($ch, CURLOPT_COOKIEFILE, 1);
-
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
-
- curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
-
- curl_setopt($ch, CURLOPT_USERAGENT, ‘Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)’);
-
- $http_header = array();
- $http_header[] = ‘Connection: Keep-Alive’;
- $http_header[] = ‘Pragma: no-cache’;
- $http_header[] = ‘Cache-Control: no-cache’;
- $http_header[] = ‘Accept: */*’;
- $http_header[] = ‘Host: ‘.$url_ary['host'];
- curl_setopt($ch, CURLOPT_HTTPHEADER, $http_header);
-
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
-
- curl_setopt($ch, CURLOPT_REFERER, $url);
-
- curl_setopt($ch, CURLOPT_URL, $url);
-
2、命令下行指定用户组来执行命令
最偷懒的方法就是:su www -c 'php xxx.php'
3、hightman遇到的Javascript函数parseInt(''),返回NaN,这个让我想起有个人做了一个PPT,来说明JS中的一些特殊问题,它的地址是:http://neatstudio.com/show-1987-1.shtml
4、PHP的header跳转
大家都知道header("Location:http://xxx.xxx.com");在这个之后如果你file_put_contents内容去一个文件,还是会被执行的,于是我们习惯性的在header跳转后继续处理一些内容,即不影响跳转,也可以完成一些内容性的处理。
但尝试了一下,如果是header()下面跟上sleep(10);你会发现header不会即刻跳转了,会等10秒后再跳转。
因此,现在了解header跳转还是会受原有代码的影响的。
5、HTML5的a标签属性ping
ping的属性说的很好,当a有href属性时,如果有ping属性,会在跳转链接时候,自动ping网站(ping标签中的对应网站),但测试了一下之后发现。居然没用。查看了一下资料,我操:
6.6 Changes from 4 March 2010 to 24 June 2010
- The
ping
attribute has been removed from the W3C version of HTML5.
我郁闷啊。。。
-------EOF------
在使用PHP进行POST和GET的时候,如果为了简单的应用,大多情况下,我们都采用了fsockopen,于是snoopy就成了我们的最佳选择。可惜,并非所有POST都可以通过snoopy来完成。毕竟,在snoopy中,POST的方式,即类似FORM的entype只支持两种 :application/x-www-form-urlencoded和multipart/form-data,一个是针对FORM中的变量,一个是针对文件上传。一般来说这两种足够了。但昨天发现了一个问题。那就是。。。http://www.baidu.com/search/blogsearch_help.html#n7
baidu和google其他一样,都提供了blogsearch的ping服务,因此,你发表博客后,可以通知一下百度,你的博客更新了。
于是昨天尝试着用snoopy来进行POST发送。结果。。。一直失败,提示无法连接80端口,再看一下百度的说明:
XML/HTML代码
- ping-service对非POST方法请求返回HTTP_METHOD_NOT_ALLOWED(405)错误代码,对超大错误包返回 HTTP_REQUEST_ENTITY_TOO_LARGE(413)错误代码,对非“text/xml”请求包返回 HTTP_UNSUPPORTED_MEDIA_TYPE(415)错误代码。
- 其他情况返回HTTP_OK(200)代码,xml-rpc响应http包体为一个xml文档,含有一个int值,0表示推送成功,其他值表示推送失败,目前只有0和1。
而snoopy在连接的时候是需要先fsockopen,打开80端口的连接后才能发送数据。。。所以,死活连接不上了。最后,用了curl。。。短短几行代码就解决了。。
事实上,即使snoopy能够连接上百度的ping服务,也没有办法提交,因为$snoopy->submit()方法的第二个参数必须是数组。众所周知,FORM提交都是由name对应着value的。snoopy正是这样操作的,他无法直接把一个字符串不对应name就提交。。。
以下是curl代码:
XML/HTML代码
- set_time_limit( 0 );
- $url = "http://ping.baidu.com/ping/RPC2";
- $data = '<?xml version="1.0" encoding="UTF-8"?>
- <methodCall>
- <methodName>weblogUpdates.extendedPing</methodName>
- <params>
- <param>
- <value><string>百度的空间</string></value>
- </param>
- <param>
- <value><string>http://hi.baidu.com/baidu/</string></value>
- </param>
- <param>
- <value><string>http://hi.baidu.com/baidu/blog/item/5e8b10d574e971cd50da4b74.html</string></value>
- </param>
- <param>
- <value><string>http://hi.baidu.com/baidu/rss</string></value>
- </param>
- </params>
- </methodCall>';
-
-
-
- $header[]="Content-Type: text/xml; charset=utf-8";
-
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_HEADER, 0);
- $res = curl_exec($ch);
- curl_close($ch);
- echo '<pre>';
- print_r( htmlspecialchars($res) );
- echo '</pre>';
PHP函数库里面,提到CURL,恐怕很多人都会翘起大拇指吧,确实,这个函数库太牛叉了
CURL其实是调用的CURL的lib,随着PHP版本的升高,curl所需的lib版本也随之提高。
关于CURL所必须的类库和安装说明,手册上有详细介绍:
XML/HTML代码
- Requirements
-
- In order to use PHP's cURL functions you need to install the libcurl package. PHP requires that you use libcurl 7.0.2-beta or higher. In PHP 4.2.3, you will need libcurl version 7.9.0 or higher. From PHP 4.3.0, you will need a libcurl version that's 7.9.8 or higher. PHP 5.0.0 requires a libcurl version 7.10.5 or greater.
-
- Installation
-
- To use PHP's cURL support you must also compile PHP --with-curl[=DIR] where DIR is the location of the directory containing the lib and include directories. In the "include" directory there should be a folder named "curl" which should contain the easy.h and curl.h files. There should be a file named libcurl.a located in the "lib" directory. Beginning with PHP 4.3.0 you can configure PHP to use cURL for URL streams --with-curlwrappers.
-
- Note to Win32 Users: In order to enable this module on a Windows environment, libeay32.dll and ssleay32.dll must be present in your PATH.
- You don't need libcurl.dll from the cURL site.
然后在使用的时候也很方便,只需要初始化一下,设置一下postfields或者GET啥啥的,最后exec一下就行了。关键是别忘了close.
例子代码如下:
PHP代码
- $ch = curl_init("http://www.example.com/");
- $fp = fopen("example_homepage.txt", "w");
-
- curl_setopt($ch, CURLOPT_FILE, $fp);
- curl_setopt($ch, CURLOPT_HEADER, 0);
-
- curl_exec($ch);
- curl_close($ch);
- fclose($fp);
以上例子代码有点特殊,是因为他把网页内容进行了下载,同时生成一个文件。这是默认调用GET的方法。
其实,CURL更多的是用来处理POST数据、上传文件等功能
例子如下:
PHP代码
- <?
-
-
-
-
-
-
- $url = 'http://www.ericgiguere.com/tools/http-header-viewer.html';
-
-
- function disguise_curl($url)
- {
- $curl = curl_init();
-
-
-
- $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
- $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
- $header[] = "Cache-Control: max-age=0";
- $header[] = "Connection: keep-alive";
- $header[] = "Keep-Alive: 300";
- $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
- $header[] = "Accept-Language: en-us,en;q=0.5";
- $header[] = "Pragma: ";
-
- curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_USERAGENT, 'Googlebot/2.1 (+http://www.google.com/bot.html)');
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
- curl_setopt($curl, CURLOPT_REFERER, 'http://www.google.com');
- curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
- curl_setopt($curl, CURLOPT_AUTOREFERER, true);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_TIMEOUT, 10);
-
- $html = curl_exec($curl);
- curl_close($curl);
-
- return $html;
- }
-
-
- $text = disguise_curl($url);
- echo $text;
- ?>
上面是一个比较完整的实现。特别需要注意的是header头部的发送,最初看手册的时候,我一以为CURLOPT_HTTPHEADER所需的数组是键值对应的,即:
PHP代码
- $header = array('Keep-Alive'=>'300');
现实的残酷告诉我,不应该这么用,而是象上面的例子那样,每条header为数组的一个记录。
切记切记啊