基于PHP的cURL快速入门教程 (小偷采集程序)


Posted in PHP onJune 02, 2011

最爽的是,PHP也支持 cURL 库。本文将介绍 cURL 的一些高级特性,以及在PHP中如何运用它。

为什么要用 cURL?

是的,我们可以通过其他办法获取网页内容。大多数时候,我因为想偷懒,都直接用简单的PHP函数:

$content = file_get_contents("https://3water.com");// or$lines = file("https://3water.com");// orreadfile(https://3water.com);

不过,这种做法缺乏灵活性和有效的错误处理。而且,你也不能用它完成一些高难度任务——比如处理coockies、验证、表单提交、文件上传等等。

引用:
cURL 是一种功能强大的库,支持很多不同的协议、选项,能提供 URL 请求相关的各种细节信息。

基本结构

在学习更为复杂的功能之前,先来看一下在PHP中建立cURL请求的基本步骤:

  1. 初始化
  2. 设置变量
  3. 执行并获取结果
  4. 释放cURL句柄

// 1. 初始化$ch = curl_init();// 2. 设置选项,包括URLcurl_setopt($ch, CURLOPT_URL, "https://3water.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);}// ...

请注意,比较的时候我们用的是“=== FALSE”,而非“== FALSE”。因为我们得区分 空输出 和 布尔值FALSE,后者才是真正的错误。

获取信息

这是另一个可选的设置项,能够在cURL执行后获取这一请求的有关信息:

// ...curl_exec($ch);$info = curl_getinfo($ch);echo '获取'. $info['url'] . '耗时'. $info['total_time'] . '秒';// ...

返回的数组中包括了以下信息:

  • “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.cnn.com",    "http://www.mozilla.com",    "http://www.facebook.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: $url\n";    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 redirection\n";        }    }    echo "\n\n";}

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

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

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

基于PHP的cURL快速入门教程 (小偷采集程序)

用POST方法发送数据

当发起GET请求时,数据可以通过“查询字串”(query string)传递给一个URL。例如,在google中搜索时,搜索关键即为URL的查询字串的一部分:

http://www.google.com/search?q=nettuts

这种情况下你可能并不需要cURL来模拟。把这个URL丢给“file_get_contents()”就能得到相同结果。

不过有一些HTML表单是用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);// 我们在POST数据哦!curl_setopt($ch, CURLOPT_POST, 1);// 把post的变量加上curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);$output = curl_exec($ch);curl_close($ch);echo $output;

执行代码后应该会得到以下结果:

基于PHP的cURL快速入门教程 (小偷采集程序)

这段脚本发送一个POST请求给 post_output.php ,这个页面 $_POST 变量并返回,我们利用cURL捕捉了这个输出。

文件上传

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

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

print_r($_FILES);

以下是真正执行文件上传任务的脚本:

$url = "http://localhost/upload_output.php";$post_data = array (    "foo" => "bar",    // 要上传的本地文件地址    "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变量一样传过去,不过记得在前面加上@符号。执行这段脚本应该会得到如下输出:

基于PHP的cURL快速入门教程 (小偷采集程序)

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。

还是来看一看怎么把这一功能用到实处吧:

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;// 初始化一些变量$url_list = array();$working_urls = array();$dead_urls = array();$not_found_urls = array();$active = null;// 连到 MySQLif (!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());}// 找出所有含有链接的文章$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)) {    // 用正则匹配链接    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;        }    }}// 移除重复链接$url_list = array_values(array_unique($url_list));if (!$url_list) {    die('No URL to check');}

我们首先配置好数据库,一系列要排除的域名($excluded_domains),以及最大并发连接数($max_connections)。然后,连接数据库,获取文章和包含的链接,把它们收集到一个数组中($url_list)。

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

// 1. 批处理器$mh = curl_multi_init();// 2. 加入需批量处理的URLfor ($i = 0; $i

下面解释一下以上代码。列表的序号对应着代码注释中的顺序数字。

  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没处理。

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

基于PHP的cURL快速入门教程 (小偷采集程序)

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

另一些有用的cURL 选项

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);

翻墙术

你可以用代理发起cURL请求:

$ch = curl_init();curl_setopt($ch, CURLOPT_URL,'http://www.example.com');curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 指定代理地址curl_setopt($ch, CURLOPT_PROXY, '11.11.11.11:8080');// 如果需要的话,提供用户名和密码curl_setopt($ch, CURLOPT_PROXYUSERPWD,'user:pass');$output = curl_exec($ch);curl_close ($ch);

回调函数

可以在一个URL请求过程中,让cURL调用某指定的回调函数。例如,在内容或者响应下载的过程中立刻开始利用数据,而不用等到完全下载完。

$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);}

这个回调函数必须返回字串的长度,不然此功能将无法正常使用。

在URL响应接收的过程中,只要收到一个数据包,这个函数就会被调用。

小结

今天我们一起学习了cURL库的强大功能和灵活的扩展性。希望你喜欢。下一次要发起URL请求时,考虑下cURL吧!

原文:基于PHP的cURL快速入门

英文原文:http://net.tutsplus.com/tutorial%20...%20for-mastering-curl/

原文作者:Burak Guzel

PHP 相关文章推荐
PHP4引用文件语句的对比
Oct 09 PHP
php设计模式 Prototype (原型模式)代码
Jun 26 PHP
php正则表达式(regar expression)
Sep 10 PHP
PHP+Ajax异步通讯实现用户名邮箱验证是否已注册( 2种方法实现)
Dec 28 PHP
PHP反转字符串函数strrev()函数的用法
Feb 04 PHP
PHP迭代器实现斐波纳契数列的函数
Nov 12 PHP
php生成图片验证码的实例讲解
Aug 03 PHP
修复ShopNC使用QQ 互联时提示100010 错误
Nov 08 PHP
php array_values 返回数组的值实例详解
Nov 17 PHP
PHP聚合式迭代器接口IteratorAggregate用法分析
Dec 28 PHP
php中html_entity_decode实现HTML实体转义
Jun 13 PHP
php+laravel 扫码二维码签到功能
May 15 PHP
PHP curl_setopt()函数实例代码与参数分析
Jun 02 #PHP
php小技巧 把数组的键和值交换形成了新的数组,查找值取得键
Jun 02 #PHP
使ecshop模板中可引用常量的实现方法
Jun 02 #PHP
php 数组使用详解 推荐
Jun 02 #PHP
php smarty 二级分类代码和模版循环例子
Jun 01 #PHP
PHP 八种基本的数据类型小结
Jun 01 #PHP
Apache2中实现多网站域名绑定的实现方法
Jun 01 #PHP
You might like
很实用的一个完整email发送程序
2006/10/09 PHP
PHP仿微信多图片预览上传实例代码
2016/09/13 PHP
DWZ+ThinkPHP开发时遇到的问题分析
2016/12/12 PHP
JavaScript DOM 学习第三章 内容表格
2010/02/19 Javascript
js的逻辑运算符 ||
2010/05/31 Javascript
JS完成代码前最好对其做5件事
2013/04/07 Javascript
利用javascript实现禁用网页上所有文本框,下拉菜单,多行文本域
2013/12/14 Javascript
js实现倒计时时钟的示例代码
2013/12/17 Javascript
JS实现向表格中动态添加行的方法
2015/03/30 Javascript
配置Grunt的Task时通配符支持和动态生成文件名问题
2015/09/06 Javascript
JavaScript中Number对象的toFixed() 方法详解
2016/09/02 Javascript
javascript读取文本节点方法小结
2016/12/15 Javascript
微信小程序实现图片预加载组件
2017/01/18 Javascript
深入理解vue2.0路由如何配置问题
2017/07/18 Javascript
浅谈微信JS-SDK 微信分享接口开发(介绍版)
2018/08/15 Javascript
原生JS实现旋转轮播图+文字内容切换效果【附源码】
2018/09/29 Javascript
微信打开网址添加在浏览器中打开提示的办法
2019/05/20 Javascript
你或许不知道的一些npm实用技巧
2019/07/04 Javascript
vue悬浮可拖拽悬浮按钮的实例代码
2019/08/20 Javascript
在Layui中操作数据表格,给指定单元格添加事件示例
2019/10/26 Javascript
js最全的数组的降维5种办法(小结)
2020/04/28 Javascript
Python的Flask框架与数据库连接的教程
2015/04/20 Python
使用beaker让Facebook的Bottle框架支持session功能
2015/04/23 Python
python将秒数转化为时间格式的实例
2018/09/16 Python
tensor和numpy的互相转换的实现示例
2019/08/02 Python
基于Python的图像阈值化分割(迭代法)
2020/11/20 Python
英国家居用品和家居装饰品购物网站:Cox & Cox
2019/08/25 全球购物
Prototype中如何为一个元素添加一个方法
2014/12/08 面试题
排序都有哪几种方法?请列举。用JAVA实现一个快速排序
2014/02/16 面试题
好的演讲稿开场白
2013/12/30 职场文书
公务员转正考察材料
2014/02/07 职场文书
采购类个人求职的自我评价
2014/02/18 职场文书
党员承诺书内容
2014/03/26 职场文书
公务员四风问题对照检查材料整改措施
2014/09/26 职场文书
网吧管理制度范本
2015/08/05 职场文书
2016猴年春节问候语
2015/11/11 职场文书