cURL基础教程
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中使用的基本步骤:
- Initialize(初始化)
- Set Options(设置参数)
- Execute and Fetch Result(执行和获取结果)
- 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; |
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); |
这个示例中有两个主要循环。第一个 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’); } |
下面的代码有点复杂,因此将一步一小步地详细解释:
以下是代码片段: // 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; } } |
- 新建一个批处理器。Created a multi handle.
- 稍后我们将创建一个把URL加入批处理器的函数 add_url_to_multi_handle() 。每当这个函数被调用,就有一个新url被加入批处理器。一开始,我们给批处理器添加了10个URL(这一数字由 $max_connections 所决定)。
- 运行 curl_multi_exec() 进行初始化工作是必须的,只要它返回 CURLM_CALL_MULTI_PERFORM 就还有事情要做。这么做主要是为了创建连接,它不会等待完整的URL响应。
- 只要批处理中还有活动连接主循环就会一直持续。
- curl_multi_select() 会一直等待,直到某个URL查询产生活动连接。
- cURL的活儿又来了,主要是获取响应数据。
- 检查各种信息。当一个URL请求完成时,会返回一个数组。
- 在返回的数组中有一个 cURL 句柄。我们利用其获取单个cURL请求的相应信息。
- 如果这是一个死链或者请求超时,不会返回http状态码。
- 如果这个页面找不到了,会返回404状态码。
- 其他情况我们都认为这个链接是可用的(当然,你也可以再检查一下500错误之类…)。
- 从该批次移除这个cURL句柄,因为它已经没有利用价值了,关了它!
- 很好,现在可以另外加一个URL进来了。再一次地,初始化工作又开始进行…
- 嗯,该干的都干了。关闭批处理器,生成报告。
- 回过头来看给批处理器添加新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/
建议继续学习:
- 用Hyer来进行网站的抓取 (阅读:157241)
- Rolling cURL: PHP并发最佳实践 (阅读:10441)
- curl 命令使用cookie (阅读:8768)
- 抓取网页内容生成Kindle电子书 (阅读:8439)
- 淘宝搜索:定向抓取网页技术漫谈 (阅读:8272)
- Python抓取框架:Scrapy的架构 (阅读:7754)
- curl检查访问网页返回的状态码 (阅读:6601)
- 定向抓取漫谈 (阅读:4463)
- 快速构建实时抓取集群 (阅读:4386)
- php实现百度音乐采集下载 (阅读:4413)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:标点符 来源: 标点符
- 标签: curl 抓取 采集
- 发布时间:2010-04-07 09:16:30
- [69] Twitter/微博客的学习摘要
- [67] IOS安全–浅谈关于IOS加固的几种方法
- [65] 如何拿下简短的域名
- [65] android 开发入门
- [63] find命令的一点注意事项
- [62] Go Reflect 性能
- [61] 流程管理与用户研究
- [60] Oracle MTS模式下 进程地址与会话信
- [59] 图书馆的世界纪录
- [57] 读书笔记-壹百度:百度十年千倍的29条法则