基于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 相关文章推荐
实用函数9
Nov 08 PHP
php读取30天之内的根据算法排序的代码
Apr 06 PHP
PHP 反向排序和随机排序代码
Jun 30 PHP
PHP执行zip与rar解压缩方法实现代码
Dec 05 PHP
探讨php define()函数及defined()函数使用详解
Jun 09 PHP
PHP 获取文件权限函数介绍
Jul 11 PHP
PHP文件上传主要代码讲解
Sep 30 PHP
PHP中的Streams详细介绍
Nov 12 PHP
PHP+Ajax实时自动检测是否联网的方法
Jul 01 PHP
PHP使用逆波兰式计算工资的方法
Jul 29 PHP
PHP中ltrim与rtrim去除左右空格及特殊字符实例
Jan 07 PHP
快速解决PHP调用Word组件DCOM权限的问题
Dec 27 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
php 过滤器实现代码
2010/08/09 PHP
神盾加密解密教程(二)PHP 神盾解密
2014/06/08 PHP
在php7中MongoDB实现模糊查询的方法详解
2017/05/03 PHP
Extjs 几个方法的讨论
2010/01/28 Javascript
jquery访问ashx文件示例代码
2014/08/11 Javascript
JavaScript function 的 length 属性使用介绍
2014/09/15 Javascript
Js为表单动态添加节点内容的方法
2015/02/10 Javascript
jQuery鼠标悬浮链接弹出跟随图片实例代码
2016/01/08 Javascript
js获取所有checkbox的值的简单实例
2016/05/30 Javascript
jQuery实现磁力图片跟随效果完整示例
2016/09/16 Javascript
基于 Vue 的树形选择组件的示例代码
2017/08/18 Javascript
vue中路由参数传递可能会遇到的坑
2017/12/07 Javascript
浅谈redux, koa, express 中间件实现对比解析
2019/05/23 Javascript
关于微信小程序获取小程序码并接受buffer流保存为图片的方法
2019/06/07 Javascript
小程序实现分类页
2019/07/12 Javascript
uni-app 支持多端第三方地图定位的方法
2020/01/03 Javascript
[02:32]DOTA2亚洲邀请赛 C9战队出场宣传片
2015/02/07 DOTA
Django实现图片文字同时提交的方法
2015/05/26 Python
Flask数据库迁移简单介绍
2017/10/24 Python
浅谈python爬虫使用Selenium模拟浏览器行为
2018/02/23 Python
Django+uni-app实现数据通信中的请求跨域的示例代码
2019/10/12 Python
PYTHON发送邮件YAGMAIL的简单实现解析
2019/10/28 Python
Python 简单计算要求形状面积的实例
2020/01/18 Python
利用HTML5+CSS3实现3D转换效果实例详解
2017/05/02 HTML / CSS
香蕉共和国工厂店:Banana Republic Factory
2018/06/09 全球购物
屈臣氏官方旗舰店:亚洲享负盛名的保健及美妆零售商
2019/03/15 全球购物
企业行政文员岗位职责
2013/12/03 职场文书
关于廉洁的广播稿
2014/01/30 职场文书
写给老婆的检讨书
2014/02/21 职场文书
感谢信格式范文
2015/01/22 职场文书
小学教师师德师风自我评价
2015/03/04 职场文书
幼儿园门卫安全责任书
2015/05/08 职场文书
当你找不到方向的时候,不妨读读刘备的一生
2019/08/05 职场文书
成人成长感言如何写?
2019/08/16 职场文书
800字作文之大雪
2019/12/04 职场文书
使用Django实现商城验证码模块的方法
2021/06/01 Python