技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> PHP --> cURL基础教程

cURL基础教程

浏览:3847次  出处信息

cURL 是一个支持包括HTTP、FTP、TELNET等多种协议使用URL语法规定来传输文件和数据的工具。令人高兴的是,cURL(supported by PHP)被PHP支持。下面将介绍一些在PHP中使用cURL的方法。

为什么要使用cURL

以下是代码片段:
$content = file_get_contents("http://www.biaodianfu.com");
 
// or
 
$content = file("http://www.biaodianfu.com");
 
// or
 
$content = readfile(http://www.biaodianfu.com);

采用上述方法也可以,获取文件。但是用PHP自带的函数却无法处理coockies、验证、表单提交、文件上传等操作。

基本结构

在实现更高技能的功能前,我们先来学习下cURL在PHP中使用的基本步骤:

  1. Initialize(初始化)
  2. Set Options(设置参数)
  3. Execute and Fetch Result(执行和获取结果)
  4. Free up the cURL handle(释放cURL句柄)
以下是代码片段:
// 1. initialize
 $ch = curl_init();
 
// 2. set the options, including the url
 curl_setopt($ch, CURLOPT_URL, "http://www.biaodianfu.com");
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 curl_setopt($ch, CURLOPT_HEADER, 0);
 
// 3. execute and fetch the resulting HTML output
 $output = curl_exec($ch);
 
// 4. free up the curl handle
 curl_close($ch);

第二步,将会是我们这篇文章最重要的部分,英文下面就是见证奇迹的时刻。我们有一大堆用来指定URL请求的cURL(long list of cURL options )选项可以设置。想一下子把所有的设置项学习完事有些困难的,今天我们只学习一些常用的功能。

监测错误

以下是代码片段:
// ...
 
$output = curl_exec($ch);
 
if ($output === FALSE) {
 
echo "cURL Error: " . curl_error($ch);
 
}
 
// ..

注意:我们比较的时候用的是“=== FALSE”,而非“== FALSE”。因为我们得区分空输出和布尔值FALSE。

获取信息

另外一个设置是在执行以后,获取curl请求信息。

以下是代码片段:
// ...
 
curl_exec($ch);
 
$info = curl_getinfo($ch);
 
echo ’Took ’ . $info[’total_time’] . ’ seconds for url ’ . $info[’url’];
 
// ...

返回信息包含在下面的数组里:

  • “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。

以下是代码片段:
// test URLs
 $urls = array(
 "http://www.cnn.com",
 "http://www.mozilla.com",
 "http://www.facebook.com"
 );
 // test browsers
 $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: $url\n";
 
foreach ($browsers as $test_name => $browser) {
 
$ch = curl_init();
 
// set url
 curl_setopt($ch, CURLOPT_URL, $url);
 
// set browser specific headers
 curl_setopt($ch, CURLOPT_HTTPHEADER, array(
 "User-Agent: {$browser[’user_agent’]}",
 "Accept-Language: {$browser[’language’]}"
 ));
 
// we don’t want the page contents
 curl_setopt($ch, CURLOPT_NOBODY, 1);
 
// we need the HTTP Header returned
 curl_setopt($ch, CURLOPT_HEADER, 1);
 
// return the results instead of outputting it
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 
$output = curl_exec($ch);
 
curl_close($ch);
 
// was there a redirection HTTP header?
 if (preg_match("!Location: (.*)!", $output, $matches)) {
 
echo "$test_name: redirects to $matches[1]\n";
 
} else {
 
echo "$test_name: no redirection\n";
 
}
 
}
 echo "\n\n";
 }

首先,建立一组测试URL,接着建立一组测试浏览器信息。最后通过循环测试各种URL和浏览器匹配可能产生的情况。

因为指定了cURL选项,所以返回的输出内容只包括HTTP头信息(被存放于 $output 中)。利用一个简单的正则表达式,检查这个头信息中是否包含了“Location:”。

运行这段代码会返回如下结果:

POST方式提交数据

当使用GET方式提交数据时,数据可以通过“查询字串”(query string)传递给URL。例如,在google中搜索时,搜索关键即为URL的查询字串的一部分:

以下是代码片段:
http://www.google.com/search?q=biaodianfu

这种情况下你可能并不需要cURL来进行处理。“file_get_contents()”这个函数就能搞定所有的事情。

对于一些使用POST方法提交的表单。数据是通过 HTTP请求体(request body) 发送,而不是查询字串。例如,在使用CodeIgniter论坛的表单,无论你输入什么关键字,总是被POST到如下页面:

以下是代码片段:
http://codeigniter.com/forums/do_search/

我们写一段PHP脚程序来模拟这种URL请求。首先,我们新建一个可以接受并显示POST数据的文件,我们把它命名为post_output.php:

以下是代码片段:
print_r($_POST);

接下来,写一段PHP程序来执行cURL请求:

以下是代码片段:
$url = "http://localhost/post_output.php";
 
$post_data = array (
 "foo" => "bar",
 "query" => "Nettuts",
 "action" => "Submit"
 );
 
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL, $url);
 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 // we are doing a POST request
 curl_setopt($ch, CURLOPT_POST, 1);
 // adding the post variables to the request
 curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
 
$output = curl_exec($ch);
 
curl_close($ch);
 
echo $output;
当您运行上面的程序以后,会得到以下结果:

文件上传

上传文件和上面的POST十分相似。因为所有的文件上传表单都是通过POST方法提交的。

首先新建一个接收文件的页面,命名为 upload_output.php:

以下是代码片段:
print_r($_FILES);

下面是一段上传程序:

以下是代码片段:
$url = "http://localhost/upload_output.php";
 
$post_data = array (
 "foo" => "bar",
 // file to be uploaded
 "upload" => "@C:/wamp/www/test.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多线程处理

cURL的一个高级特性是多线程处理,批处理句柄(handle)。这一特性允许你同时或异步地打开多个URL连接。

Let’s look at this

这个是来自php.net的示例代码(sample code from php.net):

以下是代码片段:
// create both cURL resources
 $ch1 = curl_init();
 $ch2 = curl_init();
 
// set URL and other appropriate options
 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);
 
//create the multiple cURL handle
 $mh = curl_multi_init();
 
//add the two handles
 curl_multi_add_handle($mh,$ch1);
 curl_multi_add_handle($mh,$ch2);
 
$active = null;
 //execute the handles
 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);
 }
 }
 
//close the handles
 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。

接下来我们要看看是否可以把我们学到的应用到我们的工作中:

WordPress 链接检测

想象一下你有一个文章数目庞大的博客,这些文章中包含了大量外部网站链接。一段时间之后,因为这样那样的原因,这些链接中相当数量都失效了。下面我们建立一个程序,分析所有这些链接,找出打不开或者404的网站/网页,并生成一个报告。

请注意,以下并不是一个真正可用的WordPress插件,仅是一段提供演示的独立程序。

好,开始吧。首先,从数据库中读取所有这些链接:

以下是代码片段:
// CONFIG
 $db_host = ’localhost’;
 $db_user = ’root’;
 $db_pass = ’’;
 $db_name = ’wordpress’;
 $excluded_domains = array(
 ’localhost’, ’www.mydomain.com’);
 $max_connections = 10;
 // initialize some variables
 $url_list = array();
 $working_urls = array();
 $dead_urls = array();
 $not_found_urls = array();
 $active = null;
 
// connect to MySQL
 if (!mysql_connect($db_host, $db_user, $db_pass)) {
 die(’Could not connect: ’ . mysql_error());
 }
 if (!mysql_select_db($db_name)) {
 die(’Could not select db: ’ . mysql_error());
 }
 
// get all published posts that have links
 $q = "SELECT post_content FROM wp_posts
 WHERE post_content LIKE ’%href=%’
 AND post_status = ’publish’
 AND post_type = ’post’";
 $r = mysql_query($q) or die(mysql_error());
 while ($d = mysql_fetch_assoc($r)) {
 
// get all links via regex
 if (preg_match_all("!href=\"(.*?)\"!", $d[’post_content’], $matches)) {
 
foreach ($matches[1] as $url) {
 
// exclude some domains
 $tmp = parse_url($url);
 if (in_array($tmp[’host’], $excluded_domains)) {
 continue;
 }
 
// store the url
 $url_list []= $url;
 }
 }
 }
 
// remove duplicates
 $url_list = array_values(array_unique($url_list));
 
if (!$url_list) {
 die(’No URL to check’);
 }
我们首先配置好数据库,设置一系列要排除的域名($excluded_domains),以及最大并发连接数($max_connections)。然后,连接数据库,获取文章和包含的链接,把它们收集到一个数组中($url_list)。

下面的代码有点复杂,因此将一步一小步地详细解释:

以下是代码片段:
// 1. multi handle
 $mh = curl_multi_init();
 
// 2. add multiple URLs to the multi handle
 for ($i = 0; $i < $max_connections; $i++) {
 add_url_to_multi_handle($mh, $url_list);
 }
 
// 3. initial execution
 do {
 $mrc = curl_multi_exec($mh, $active);
 } while ($mrc == CURLM_CALL_MULTI_PERFORM);
 
// 4. main loop
 while ($active && $mrc == CURLM_OK) {
 
// 5. there is activity
 if (curl_multi_select($mh) != -1) {
 
// 6. do work
 do {
 $mrc = curl_multi_exec($mh, $active);
 } while ($mrc == CURLM_CALL_MULTI_PERFORM);
 
// 7. is there info?
 if ($mhinfo = curl_multi_info_read($mh)) {
 // this means one of the requests were finished
 
// 8. get the info on the curl handle
 $chinfo = curl_getinfo($mhinfo[’handle’]);
 
// 9. dead link?
 if (!$chinfo[’http_code’]) {
 $dead_urls []= $chinfo[’url’];
 
// 10. 404?
 } else if ($chinfo[’http_code’] == 404) {
 $not_found_urls []= $chinfo[’url’];
 
// 11. working
 } else {
 $working_urls []= $chinfo[’url’];
 }
 
// 12. remove the handle
 curl_multi_remove_handle($mh, $mhinfo[’handle’]);
 curl_close($mhinfo[’handle’]);
 
// 13. add a new url and do work
 if (add_url_to_multi_handle($mh, $url_list)) {
 
do {
 $mrc = curl_multi_exec($mh, $active);
 } while ($mrc == CURLM_CALL_MULTI_PERFORM);
 }
 }
 }
 }
 
// 14. finished
 curl_multi_close($mh);
 
echo "==Dead URLs==\n";
 echo implode("\n",$dead_urls) . "\n\n";
 
echo "==404 URLs==\n";
 echo implode("\n",$not_found_urls) . "\n\n";
 
echo "==Working URLs==\n";
 echo implode("\n",$working_urls);
 
// 15. adds a url to the multi handle
 function add_url_to_multi_handle($mh, $url_list) {
 static $index = 0;
 
// if we have another url to get
 if ($url_list[$index]) {
 
// new curl handle
 $ch = curl_init();
 
// set the url
 curl_setopt($ch, CURLOPT_URL, $url_list[$index]);
 // to prevent the response from being outputted
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 // follow redirections
 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
 // do not need the body. this saves bandwidth and time
 curl_setopt($ch, CURLOPT_NOBODY, 1);
 
// add it to the multi handle
 curl_multi_add_handle($mh, $ch);
 
// increment so next url is used next time
 $index++;
 
return true;
 } else {
 
// we are done adding new URLs
 return false;
 }
 }
 
下面解释一下以上代码。列表的序号对应着代码注释中的顺序数字。
  1. 新建一个批处理器。Created a multi handle.
  2. 稍后我们将创建一个把URL加入批处理器的函数 add_url_to_multi_handle() 。每当这个函数被调用,就有一个新url被加入批处理器。一开始,我们给批处理器添加了10个URL(这一数字由 $max_connections 所决定)。
  3. 运行 curl_multi_exec()  进行初始化工作是必须的,只要它返回 CURLM_CALL_MULTI_PERFORM 就还有事情要做。这么做主要是为了创建连接,它不会等待完整的URL响应。
  4. 只要批处理中还有活动连接主循环就会一直持续。
  5. curl_multi_select() 会一直等待,直到某个URL查询产生活动连接。
  6. cURL的活儿又来了,主要是获取响应数据。
  7. 检查各种信息。当一个URL请求完成时,会返回一个数组。
  8. 在返回的数组中有一个 cURL 句柄。我们利用其获取单个cURL请求的相应信息。
  9. 如果这是一个死链或者请求超时,不会返回http状态码。
  10. 如果这个页面找不到了,会返回404状态码。
  11. 其他情况我们都认为这个链接是可用的(当然,你也可以再检查一下500错误之类…)。
  12. 从该批次移除这个cURL句柄,因为它已经没有利用价值了,关了它!
  13. 很好,现在可以另外加一个URL进来了。再一次地,初始化工作又开始进行…
  14. 嗯,该干的都干了。关闭批处理器,生成报告。
  15. 回过头来看给批处理器添加新URL的函数。这个函数每调用一次,静态变量 $index 就递增一次,这样我们才能知道还剩多少URL没处理。

我把这个脚本在我的博客上跑了一遍(测试需要,有一些错误链接是故意加上的),结果如下:

共检查约40个URL,只耗费两秒不到。当需要检查更加大量的URL时,其省心省力的效果可想而知!如果你同时打开10个连接,还能再快上10倍!另外, 你还可以利用cURL批处理的无隔断特性来处理大量URL请求。

一些其他有用的cURL选项

HTTP Authentication(HTTP登录)

如果遇到有需要登录的URL,你可以使用如下方法:

以下是代码片段:
$url = "http://www.somesite.com/members/";
 
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 
// send the username and password
 curl_setopt($ch, CURLOPT_USERPWD, "myusername:mypassword");
 
// if you allow redirections
 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
 // this lets cURL keep sending the username and password
 // after being redirected
 curl_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1);
 
$output = curl_exec($ch);
 
curl_close($ch);
FTP Upload(FTP上传)

PHP自带 FTP library,不过你也可以使用cURL的方法实现:

以下是代码片段:
// open a file pointer
 $file = fopen("/path/to/file", "r");
 
// the url contains most of the info needed
 $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);
 
// upload related options
 curl_setopt($ch, CURLOPT_UPLOAD, 1);
 curl_setopt($ch, CURLOPT_INFILE, $fp);
 curl_setopt($ch, CURLOPT_INFILESIZE, filesize("/path/to/file"));
 
// set for ASCII mode (e.g. text files)
 curl_setopt($ch, CURLOPT_FTPASCII, 1);
 
$output = curl_exec($ch);
 curl_close($ch);

Using a Proxy(使用代理)

你可以通过代理提交你的URL

以下是代码片段:
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL,’http://www.example.com’);
 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 
// set the proxy address to use
 curl_setopt($ch, CURLOPT_PROXY, ’11.11.11.11:8080’);
 
// if the proxy requires a username and password
 curl_setopt($ch, CURLOPT_PROXYUSERPWD,’user:pass’);
 
$output = curl_exec($ch);
 
curl_close ($ch);
Callback Functions

It is possible to have cURL call given callback functions during the URL request, before it is finished. 例如,在请求内容在下载的时候,a在数据没有全部下载完前,就可以使用这些数据。

以下是代码片段:
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL,’http://net.tutsplus.com’);
 
curl_setopt($ch, CURLOPT_WRITEFUNCTION,"progress_function");
 
curl_exec($ch);
 
curl_close ($ch);
 
function progress_function($ch,$str) {
 
echo $str;
 return strlen($str);
 
}

为了能正常的工作,callback function必须返回字符串的长度,每次URL请求被获取,一个数据包就会被接收,callback function被请求。

原文地址(英文):http://net.tutsplus.com/tutorials/php/techniques-and-resources-for-mastering-curl/

建议继续学习:

  1. 用Hyer来进行网站的抓取    (阅读:157175)
  2. Rolling cURL: PHP并发最佳实践    (阅读:10106)
  3. curl 命令使用cookie    (阅读:8671)
  4. 抓取网页内容生成Kindle电子书    (阅读:8080)
  5. 淘宝搜索:定向抓取网页技术漫谈    (阅读:8036)
  6. Python抓取框架:Scrapy的架构    (阅读:7675)
  7. curl检查访问网页返回的状态码    (阅读:6501)
  8. 定向抓取漫谈    (阅读:4366)
  9. 快速构建实时抓取集群    (阅读:4293)
  10. PHP用CURL伪造IP和来源    (阅读:4194)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1