PHP 可以使用 file_get_content() 函数抓取网页内容,但却无法进行更复杂的处理,譬如文件的上传或下载、 Cookie 操作等等。而 cURL 提供了这些功能。
一、cURL简介
在 PHP 中,cURL 是一个扩展库。它可以与各种类型的服务器、使用各种类型的协议进行连接和通讯。
它目前支持 http、https、ftp、gopher、telnet、dict、file 和 ldap 协议,同时也支持 HTTPS 认证、HTTP POST、 FTP 上传、代理、cookies 和 用户名 + 密码的认证等。
二、cURL函数库
常用函数
函数 | 描述 |
curl_init() | 初始化 cURL 会话 |
curl_setopt() | 设置 cURL 选项 |
curl_exec() | 执行 cURL 会话 |
curl_getinfo() | 获取当前会话信息 |
curl_errno() | 返回最后一次的错误代码 |
curl_error() | 返回当前会话最后一次的错误字符串 |
curl_close() | 关闭 cURL 会话 |
其他函数
函数 | 描述 |
curl_copy_handle() | 复制一个 cURL 句柄和它的所有选项。 |
curl_escape() | 返回转义字符串,对给定的字符串进行URL编码。 |
curl_file_create() | 创建一个 CURLFile 对象。 |
curl_multi_add_handle() | 向 cURL 批处理会话中添加单独的curl句柄。 |
curl_multi_close() | 关闭一组 cURL 句柄。 |
curl_multi_exec() | 运行当前 cURL 句柄的子连接。 |
curl_multi_getcontent() | 如果设置了 CURLOPT_RETURNTRANSFER ,则返回获取的输出的文本流。 |
curl_multi_info_read() | 获取当前解析的 cURL 的相关传输信息。 |
curl_multi_init() | 返回一个新 cURL 批处理句柄。 |
curl_multi_remove_handle() | 移除 cURL 批处理句柄资源中的某个句柄资源。 |
curl_multi_select() | 等待所有 cURL 批处理中的活动连接。 |
curl_multi_setopt() | 设置一个批处理 cURL 传输选项。 |
curl_multi_strerror() | 返回描述错误码的字符串文本。 |
curl_pause() | 暂停及恢复连接。 |
curl_reset() | 重置 libcurl 的会话句柄的所有选项。 |
curl_setopt_array() | 为 cURL 传输会话批量设置选项。 |
curl_share_close() | 关闭 cURL 共享句柄。 |
curl_share_init() | 初始化 cURL 共享句柄。 |
curl_share_setopt() | 设置一个共享句柄的 cURL 传输选项。 |
curl_strerror() | 返回错误代码的字符串描述。 |
curl_unescape() | 解码 URL 编码后的字符串。 |
curl_version() | 获取 cURL 版本信息。 |
三、实现流程
1. 初始化 cURL 会话
2. 设置 cURL 选项
3. 执行 cURL 会话
4. 获取 cURL 信息和(或)错误信息(这一步可以没有)
5. 关闭 cURL 句柄
这里面最复杂的是第 2 步,cURL 的设置选项有很多,下面会结合实例来了解。查看所有选项请看这里:http://www.runoob.com/php/func-curl_setopt.html
四、实例1.GET请求
GET 请求的流程就是 cURL 的一般流程。
在本地服务器 localserver.com 根目录准备一个测试脚本 index.php,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?php
$url
=
'http://www.baidu.com'
;
// 初始化,获得一个cURL句柄
$ch
= curl_init();
// 设置选项
curl_setopt(
$ch
, CURLOPT_URL,
$url
);
// 请求URL
curl_setopt(
$ch
, CURLOPT_RETURNTRANSFER, 1);
//返回数据流,而不直接输出
curl_setopt(
$ch
, CURLOPT_HEADER, 0);
// 无需响应的header头
curl_setopt(
$ch
, CURLOPT_CONNECTTIMEOUT, 30);
//连接超时,单位为秒
// 执行并获取返回内容
$output
= curl_exec(
$ch
);
if
(
$output
=== false){
$output
=
'cURL error: '
. curl_error(
$ch
);
}
// 释放 cURL 句柄资源
curl_close(
$ch
);
print_r(
$output
);
?>
|
浏览器访问本地服务器首页 localserver.com/index.php,显示百度首页。
五、实例2.POST请求
POST 请求需要设置两个选项:
1
2
|
curl_setopt(
$ch
, CURLOPT_POST, 1);
// 表明POST请求
curl_setopt(
$ch
, CURLOPT_POSTFIELDS,
$postData
));
// POST提交数据
|
先在远程服务器 remoteserver.com 根目录准备一个用于接收的脚本 index.php,内容如下:
1
2
3
4
|
<?php
$input
=
file_get_contents
(
'php://input'
);
echo
$input
;
?>
|
然后在本地服务器 localserver.com 根目录来写用于 POST 请求的脚本 index.php,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<?php
$url
=
'http://remoteserver.com/index.php'
;
$data
=
array
(
'fname'
=>
'Daniel'
,
'lname'
=>
'Stenberg'
);
// 初始化
$ch
= curl_init();
// 设置选项
curl_setopt(
$ch
, CURLOPT_URL,
$url
);
curl_setopt(
$ch
, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch
, CURLOPT_HEADER, 0);
curl_setopt(
$ch
, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt(
$ch
, CURLOPT_POST, 1);
// POST请求
curl_setopt(
$ch
, CURLOPT_POSTFIELDS, http_build_query(
$data
));
//POST数据。用http_build_query()转换为“&”拼接的字符串
// 执行并获取返回内容
$output
= curl_exec(
$ch
);
if
(
$output
=== false){
$output
=
'cURL error: '
. curl_error(
$ch
);
}
// 释放 cURL 句柄资源
curl_close(
$ch
);
print_r(
$output
);
?>
|
浏览器访问 localserver.com/index.php,显示如下:
1
|
fname=Daniel&lname=Stenberg
|
六、实例3.上传文件
cURL 上传文件的思路是:在文件路径的前面添加"@"符号,并装在请求字段里实现上传。后台可以通过 $_FILES 获取上传文件信息。但 PHP5.6 以后,废除了"@"符号,可以使用 CURLFile 类实现上传。
先在远程服务器 remoteserver.com 根目录准备一个用于接收的脚本 index.php,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php
$action
=
$_POST
[
'action'
];
if
(
$action
==
'uploadImage'
){
$name
=
$_FILES
[
'file'
][
'name'
];
$tmpname
=
$_FILES
[
'file'
][
'tmp_name'
];
// 保存到当前脚本所在目录
move_uploaded_file(
$tmpname
, dirname(
__FILE__
).
'/'
.
$name
);
$error
=
$_FILES
[
'file'
][
'error'
];
switch
(
$error
) {
case
0:
echo
'上传成功'
;
break
;
case
1:
echo
'文件大小超出 php.ini 限制'
;
break
;
case
2:
echo
'文件大小超出 表单 MAX_FILE_SIZE 限制'
;
break
;
case
3:
echo
'文件部分被上传'
;
break
;
case
4:
echo
'没有文件被上传'
;
break
;
case
6:
echo
'找不到临时文件夹'
;
break
;
case
7:
echo
'文件写入失败'
;
break
;
default
:
$output
=
'未知错误'
;
}
}
?>
|
然后在本地服务器 localserver.com 根目录准备一个图片文件 test.jpg 和 cURL 上传脚本 index.php, 脚本内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<?php
$url
=
'http://remoteserver.com/index.php'
;
$file
=
realpath
(
getcwd
() .
'/test.jpg'
);
$data
=
array
(
'action'
=>
'uploadImage'
,
'file'
=>
'@'
.
$file
);
if
(version_compare(PHP_VERSION,
'5.6.0'
) > 0){
$data
[
'file'
] =
new
CURLFile(
$file
);
}
// 初始化
$ch
= curl_init();
// 设置选项
curl_setopt(
$ch
, CURLOPT_URL,
$url
);
curl_setopt(
$ch
, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch
, CURLOPT_HEADER, 0);
curl_setopt(
$ch
, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt(
$ch
, CURLOPT_POST, 1);
curl_setopt(
$ch
, CURLOPT_POSTFIELDS,
$data
);
// 执行并获取返回内容
$output
= curl_exec(
$ch
);
if
(
$output
=== false){
$output
=
'cURL error: '
. curl_error(
$ch
);
}
// 释放 cURL 句柄资源
curl_close(
$ch
);
print_r(
$output
);
?>
|
浏览器访问 localserver.com/index.php,显示如下:
1
|
上传成功
|
查看远程服务器根目录,发现多了一张刚才上传的图片。
七、实例4.下载文件
cURL 下载文件的一个思路是:设置 cURL 选项 CURLOPT_FILE 为一个文件指针,以此将请求的资源文件关联到一个文件流里,这个文件流一般是 fopen()函数的返回值。使用文件流将远程文件写到本地,可以避免写(下载)大文件时可能的内存出错。
在本地服务器 localserver.com 根目录来写测试脚本 index.php,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<?php
$url
=
'http://remoteserver.com/test.jpg'
;
$file
=
'./test.jpg'
;
$fp
=
fopen
(
$file
,
'w'
);
// 初始化
$ch
= curl_init();
// 设置选项
curl_setopt(
$ch
, CURLOPT_URL,
$url
);
curl_setopt(
$ch
, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch
, CURLOPT_HEADER, 0);
curl_setopt(
$ch
, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt(
$ch
, CURLOPT_FILE,
$fp
);
// 用于传输的文件流,默认是STDOUT
// 执行并获取返回内容
$output
= curl_exec(
$ch
);
if
(
$output
=== false){
$output
=
'cURL error: '
. curl_error(
$ch
);
}
// 获取已下载大小
$size_download
= curl_getinfo(
$ch
, CURLINFO_SIZE_DOWNLOAD);
// 释放资源
fclose(
$fp
);
curl_close(
$ch
);
if
(
$size_download
&&
$size_download
==
filesize
(
$file
)) {
echo
"下载成功"
;
}
else
{
echo
"下载失败或不完整"
;
}
?>
|
浏览器访问 localserver.com/index.php,显示如下:
1
|
下载成功
|
查看本地服务器根目录,发现下载到了远程的图片。
八、实例5.批处理
cURL 有一个批处理句柄,通过打开多个 cURL 句柄,并将这些句柄绑定到一个批处理句柄,然后在循环中依次处理每个 cURL 连接,可以实现异步的批处理,类似“多线程”。
在本地服务器 localserver.com 根目录来写测试脚本 index.php,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
<?php
$urls
=
array
(
'http://www.baidu.com'
,
'http://www.qidian.com'
);
$count
=
count
(
$urls
);
$ch
=
array
();
// 创建批处理cURL句柄
$mh
= curl_multi_init();
// 初始化每个cURL,并设置选项,绑定给批处理句柄
for
(
$i
= 0;
$i
<
$count
;
$i
++) {
$ch
[
$i
] = curl_init();
curl_setopt(
$ch
[
$i
], CURLOPT_URL,
$urls
[
$i
]);
curl_setopt(
$ch
[
$i
], CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch
[
$i
], CURLOPT_HEADER, 0);
curl_setopt(
$ch
[
$i
], CURLOPT_CONNECTTIMEOUT, 30);
curl_multi_add_handle(
$mh
,
$ch
[
$i
]);
}
// 执行批处理
$running
= null;
do
{
usleep(10000);
// 延迟0.01秒,单位为百万分之一秒
curl_multi_exec(
$mh
,
$running
);
// 异步实现批处理,类似“多线程”
}
while
(
$running
> 0);
// 获取每个cURL的响应
$res
=
array
();
for
(
$i
= 0;
$i
<
$count
;
$i
++) {
$res
[
$i
] = curl_multi_getcontent(
$ch
[
$i
]);
}
// 关闭全部句柄
for
(
$i
= 0;
$i
<
$count
;
$i
++) {
curl_multi_remove_handle(
$mh
,
$ch
[
$i
]);
}
curl_multi_close(
$mh
);
print_r(
$res
);
?>
|
浏览器访问 localserver.com/index.php,显示了“连在一起”的百度首页和起点网首页。