基于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 相关文章推荐
PHP 魔术函数使用说明
May 14 PHP
PHP中全局变量global和$GLOBALS[]的区别分析
Aug 06 PHP
深入PHP内存相关的功能特性详解
Jun 08 PHP
Windows下的PHP安装pear教程
Oct 24 PHP
php页面缓存方法小结
Jan 10 PHP
php获取本周星期一具体日期的方法
Apr 20 PHP
PHP实现在线阅读PDF文件的方法
Jun 17 PHP
PHP常见错误提示含义解释(实用!值得收藏)
Apr 25 PHP
PHP编写的图片验证码类文件分享
Jun 06 PHP
php获取flash尺寸详细数据的方法
Nov 12 PHP
php封装一个异常的处理类
Jun 08 PHP
PHP回调函数与匿名函数实例详解
Aug 16 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
mysql下创建字段并设置主键的php代码
2010/05/16 PHP
php checkbox 取值详细说明
2010/08/19 PHP
is_uploaded_file函数引发的不能上传文件问题
2013/10/29 PHP
ThinkPHP使用smarty模板引擎的方法
2014/07/01 PHP
php微信公众号开发之快递查询
2018/10/20 PHP
Yii框架ACF(accessController)简单权限控制操作示例
2019/04/26 PHP
PHP实现简单的协程任务调度demo示例
2020/02/01 PHP
php使用redis的几种常见操作方式和用法示例
2020/02/20 PHP
按钮JS复制文本框和表格的代码
2011/04/01 Javascript
JavaScript NaN和Infinity特殊值 [译]
2012/09/20 Javascript
THREE.JS入门教程(4)创建粒子系统
2013/01/24 Javascript
js中for in的用法示例解析
2013/12/25 Javascript
jquery实现多屏多图焦点图切换特效的方法
2015/05/04 Javascript
利用JavaScript在网页实现八数码启发式A*算法动画效果
2017/04/16 Javascript
JavaScript中使用import 和require打包后实现原理分析
2018/03/07 Javascript
深入了解JavaScript 私有化
2019/05/30 Javascript
在vue中利用v-html按分号将文本换行的例子
2019/11/14 Javascript
vue父子模板传值问题解决方法案例分析
2020/02/26 Javascript
js+canvas实现图片格式webp/png/jpeg在线转换
2020/08/22 Javascript
Python操作Oracle数据库的简单方法和封装类实例
2018/05/07 Python
Python Tkinter图形工具使用方法及实例解析
2020/06/15 Python
解析Python 偏函数用法全方位实现
2020/06/26 Python
浅谈Python里面None True False之间的区别
2020/07/09 Python
python合并多个excel文件的示例
2020/09/23 Python
实例教程 一款纯css3实现的数字统计游戏
2014/11/10 HTML / CSS
Clarks鞋澳大利亚官方网站:Clarks Australia
2019/12/25 全球购物
英国时尚高尔夫服装购物网站:Trendy Golf
2020/01/10 全球购物
墨西哥购物网站:Elektra
2020/01/21 全球购物
中职应届生会计求职信
2013/10/23 职场文书
大客户销售经理职责
2013/12/04 职场文书
工程承包协议书
2014/10/20 职场文书
请客吃饭开场白
2015/06/01 职场文书
消防演习感想
2015/08/10 职场文书
利用Nginx代理如何解决前端跨域问题详析
2021/04/02 Servers
基于Redis zSet实现滑动窗口对短信进行防刷限流的问题
2022/02/12 Redis
Oracle 11g数据库使用expdp每周进行数据备份并上传到备份服务器
2022/06/28 Oracle