curl 是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP、FTP、TELNET等。php也支持 curl 库。本文将介绍 curl 的一些高级特性,本期文章会教大家如何使用它。
为什么要用 curl?
是的,我们可以通过其他办法获取网页内容。大多数时候,我因为想偷懒,都直接用简单的PHP函数:
代码演示
$content = file_get_contents("http://www.xxx.com"); //直接获取网页上的内容
$lines = file("http://www.xxx.com");
readfile(http://www.xxx.com);
不过,这种做法缺乏灵活性和有效的错误处理。而且,你也不能用它完成一些高难度任务——比如处理coockies、验证、表单提交、文件上传等等,那么这个时候curl就派上用处。
因为curl本身是一种功能强大的库,支持很多不同的协议能提供 URL 请求相关的各种细节信息。
下面直接教大家如何使用curl
在学习更为复杂的功能之前,先来看一下请求的基本步骤:
// 1. 初始化
$ch = curl_init();
// 2. 设置选项,包括URL
curl_setopt($ch, CURLOPT_URL, "http://www.baidu.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
// 3. 执行并获取html文档内容
$output = curl_exec($ch);
// 4. 释放curl句柄
curl_close($ch);
第二步(也就是 curl_setopt() 有一长串cURL参数可供设置,它们能指定URL请求的各个细节。要一次性全部介绍完比较难,今天我们只试一下那些更常用也更有用的选项。
$output = curl_exec($ch);
if ($output === FALSE) {//检测是否错误
echo "curl Error: " . curl_error($ch);
}
curl_exec($ch);
$info = curl_getinfo($ch);//直接返回结果给info
返回的数组中包括了以下信息:
curl_setopt设置的参数
"URL" //资源网络地址
"CONTENT_TYPE" //内容编码
"HTTP_CODE" //HTTP状态码
"HEADER_SIZE" //header的大小
"REQUEST_SIZE" //请求的大小
"FILETIME" //文件创建时间
"SSL_VERIFY_RESULT" //SSL验证结果
"REDIRECT_COUNT" //跳转技术
"TOTAL_TIME" //总耗时
"NAMELOOKUP_TIME" //DNS查询耗时
"CONNECT_TIME" //等待连接耗时
"PRETRANSFER_TIME" //传输前准备耗时
"SIZE_UPLOAD" //上传数据的大小
"SIZE_DOWNLOAD" //下载数据的大小
"SPEED_DOWNLOAD" //下载速度
"SPEED_UPLOAD" //上传速度
"DOWNLOAD_CONTENT_LENGTH" /下载内容的长度
"UPLOAD_CONTENT_LENGTH" //上传内容的长度
"STARTTRANSFER_TIME" //开始传输的时间
"REDIRECT_TIME" //重定向耗时
我们利用 CURLOPT_HTTPHEADER 选项来设定我们发送出的HTTP请求头信息(http headers),包括user agent信息和默认语言。然后我们来看看这些特定网站是否会把我们重定向到不同的URL。
以下为引用的内容:
// 测试用的URL
$urls = array(
"http://www.aaa.com",
"http://www.bbb.com",
"http://www.ccc.com"
);
$browsers = array(
"standard" => array (
"user_agent" => "Mozilla/5.0 (windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)",
"language" => "en-us,en;q=0.5"
),
"iphone" => array (
"user_agent" => "Mozilla/5.0 (iPhone; U; CPU like mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A537a Safari/419.3",
"language" => "en"
),
"french" => array (
"user_agent" => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6; .NET CLR 2.0.50727)",
"language" => "fr,fr-FR;q=0.5"
)
);
foreach ($urls as $url) {
echo "URL: $urln";
foreach ($browsers as $test_name => $browser) {
$ch = curl_init();
// 设置 url
curl_setopt($ch, CURLOPT_URL, $url);
// 设置浏览器的特定header
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"User-Agent: {$browser['user_agent']}",
"Accept-Language: {$browser['language']}"
));
// 页面内容我们并不需要
curl_setopt($ch, CURLOPT_NOBODY, 1);
// 只需返回HTTP header
curl_setopt($ch, CURLOPT_HEADER, 1);
// 返回结果,而不是输出它
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
// 有重定向的HTTP头信息吗?
if (preg_match("!Location: (.*)!", $output, $matches)) {
echo "$test_name: redirects to $matches[1]n";
} else {
echo "$test_name: no redirectionn";
}
}
echo "nn";
}
用POST方法发送数据
当发起GET请求时,数据可以通过查询字串(query string)传递给一个URL:
http://www.google.com/search?q=demo
这种情况下你可能并不需要cURL来模拟。把这个URL丢给“file_get_contents()”就能得到相同结果。
不过有一些HTML表单是用POST方法提交的。这种表单提交时,数据是通过 HTTP请求体(request body) 发送,而不是查询字串。例如,当使用CodeIgniter论坛的表单,无论你输入什么关键字,总是被POST到如下页面:
http://cc.com/a/b/
你可以用PHP脚本来模拟这种URL请求。首先,新建一个可以接受并显示POST数据的文件,我们给它命名为post_output.php:
print_r($_POST);
接下来,写一段PHP脚本来执行cURL请求:
以下为引用的内容:
$url = "http://localhost/post_demo.php";
$post_data = array (
"foo" => "bar",
"query" => "demo",
"action" => "Submit"
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// 我们在POST数据哦!
curl_setopt($ch, CURLOPT_POST, 1);
// 把post的变量加上
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
这段脚本发送一个POST请求给 post_demo.php ,这个页面 $_POST 变量并返回输出。
文件上传
上传文件和前面的POST十分相似。因为所有的文件上传表单都是通过POST方法提交的。
首先新建一个接收文件的页面,命名为 upload_info.php:
print_r($_FILES);
以下为引用的内容:
$url = "http://localhost/upload_info.php";
$post_data = array (
"foo" => "bar",
// 要上传的本地文件地址
"upload" => "@C:/wamp/www/a.zip"
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
如果你需要上传一个文件,只需要把文件路径像一个post变量一样传过去,不过记得在前面加上@符号。执行这段脚本应该会得到如下输出:
cURL批处理(multi cURL)
cURL还有一个高级特性——批处理句柄(handle)。这一特性允许你同时或异步地打开多个URL连接。
下面是来自来自php.net的示例代码:
以下为引用的内容:
// 创建两个cURL资源
$ch1 = curl_init();
$ch2 = curl_init();
// 指定URL和适当的参数
curl_setopt($ch1, CURLOPT_URL, "http://lxr.php.net/");
curl_setopt($ch1, CURLOPT_HEADER, 0);
curl_setopt($ch2, CURLOPT_URL, "http://www.php.net/");
curl_setopt($ch2, CURLOPT_HEADER, 0);
// 创建cURL批处理句柄
$mh = curl_multi_init();
// 加上前面两个资源句柄
curl_multi_add_handle($mh,$ch1);
curl_multi_add_handle($mh,$ch2);
// 预定义一个状态变量
$active = null;
// 执行批处理
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 {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
// 关闭各个句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);
这里要做的就是打开多个cURL句柄并指派给一个批处理句柄。然后你就只需在一个while循环里等它执行完毕。
这个示例中有两个主要循环。第一个 do-while 循环重复调用 curl_multi_exec() 。这个函数是无隔断(non-blocking)的,但会尽可能少地执行。它返回一个状态值,只要这个值等于常量 CURLM_CALL_MULTI_PERFORM ,就代表还有一些刻不容缓的工作要做(例如,把对应URL的http头信息发送出去)。也就是说,我们需要不断调用该函数,直到返回值发生改变。
而接下来的 while 循环,只在 $active 变量为 true 时继续。这一变量之前作为第二个参数传给了 curl_multi_exec() ,代表只要批处理句柄中是否还有活动连接。接着,我们调用 curl_multi_select() ,在活动连接(例如接受服务器响应)出现之前,它都是被“屏蔽”的。这个函数成功执行后,我们又会进入另一个 do-while 循环,继续下一条URL。
HTTP 认证
如果某个URL请求需要基于 HTTP 的身份验证,你可以使用下面的代码:
复制内容到剪贴板代码:
以下为引用的内容:
$url = "http://www.somesite.com/members/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// 发送用户名和密码
curl_setopt($ch, CURLOPT_USERPWD, "myusername:mypassword");
// 你可以允许其重定向
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
// 下面的选项让 cURL 在重定向后
// 也能发送用户名和密码
curl_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1);
$output = curl_exec($ch);
curl_close($ch);
FTP 上传
PHP 自带有 FTP 类库, 但你也能用 cURL:
以下为引用的内容:
// 开一个文件指针
$file = fopen("/path/to/file", "r");
// url里包含了大部分所需信息
$url = "ftp://username:password@mydomain.com:21/path/to/new/file";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// 上传相关的选项
curl_setopt($ch, CURLOPT_UPLOAD, 1);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize("/path/to/file"));
// 是否开启ASCII模式 (上传文本文件时有用)
curl_setopt($ch, CURLOPT_FTPASCII, 1);
$output = curl_exec($ch);
curl_close($ch);