19 August 2014

随着网络应用的盛行,HTTP脚本编程成为一项流行的技术。你可能需要到某个 web 页面自动的下载一些信息(采集),并且需要模仿成一个正常浏览器的样子,甚至需要上传或 POST 数据。而cURL就是HTTP脚本编程的利器。

###什么是cURL

cURL的官方网站上这样解释:“The name is a play on ‘Client for URLs’”。意思为“访问URL的客户端”。

“cURL is a command line tool for doing all sorts of URL manipulations and transfers”,cURL是用于进行各种对URL的操作和访问传输的命令行工具。 而在Linux man手册里这样解释:“cURL - transfer a URL”,意思是对URL的访问传输。通俗的讲,我们认为 cURL 是一个强大的对 URL 进行操作的命令行工具。 而 cURL 的写法,URL 突出大写,也正是强调对 URL 的操作。

###cURL主要特点

  • 支持多协议

URL的定义为“统一资源定位符”(详见RFC 3986),是不局限于HTTP协议的。因此cURL支持多种协议,如DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS,IMAP, IMAPS,LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, TELNET and TFTP等。

  • 命令行操作,简单强大

CURL是无GUI界面的,全部基于命令行操作,这无疑使它可以很方便的集成在像Linux脚本等程序中,而通过我们进一步编程处理,就可以完成非常强大的功能。

  • 开源跨平台

cURL工具是由libcurl开源项目组提供,此项目组提供开源的libcurl库,此库是由C语言实现客户端 URL 操作库,提供对 URL 的访问等操作功能,cURL工具就是采用libcurl实现。而因为libcurl库是适用于多个平台的,因此cURL也适用于多个平台,如Win、Linux、OS/400、TPF等平台。这成为 cURL 比 GNU 官方的 wget 更强大的原因之一。

###curl命令格式:

curl [选项] [URL...]
  • -#/–progress-bar 查看下载进度条。

  • -O/–remote-name 下载并从url中提取文件名。 多个URL的情况同-o选项相同:

    curl -O -O http://www.example.com/a.html http://www.example.com/b.html curl -O http://www.example.com/a.html -O http://www.example.com/b.html

    两个URL分别保存在a.html和b.html中。

  • -C/–continue-at offset 断点续传。offset参数指定跳过多少bytes。

    curl -# -O http://exmple.com/a.pdf

下载QQ安装程序,在下载未完成时Ctrl+C结束下载,然后使用

curl -# -C - -O http://example.com/a.pdf //“-C -”指定自动检测断点续传开始位置。

进行断点续传。可看到第二次下载时并不是重新下载,而是从上次结束的地方下载。

  • -r/–range range 分块下载,指定每块下载的字节范围。此选项适用于HTTP/FTP/SFTP及本地文件。如上例中下载qq,分块下载如下:

    curl -r 0-10240 -o a.pdf http://example.com/p=ab.pdf //下载前10240bytes。 curl -r 10241-20480 -o b.pdf http://example/ao //下载10241-20480bytes。 curl -r 20481- -o qq.exe.part3 http://example/ 下载20481bytes及之后的字节。

    下载完后可使用 cat a.pdf > qq.exe的形式得到完整文件(win下需要使用copy /b)。

  • –limit-rate speed 限速下载。默认单位为bytes/s,’k’或’K’指定使用单位KB/s,可以使用’m’或’M’指定使用单位MB/s,’g’或’G’指定使用单位GB/s。如

    curl -# -O –limit-rate 100K http://example.com //指定限速为100KB/s。

限速是限制平均速度,某时刻的突发速度可能超过限制速度,但平均速度会低于限制速度。

如果同时使用了-Y/–speed-limit限制了最低下载速度,-Y/–speed-limit选择优先级更高。因此如果-Y /–speed-limit指定的值高于限制的速度,那么限制的速度回被突破,以保证最低下载速度有效(因为如果低于-Y/–speed-limit 指定的最低下载速度,下载会被取消)。

  • -y/–speed-time time -y指定超时时间,即低于最低速度多次时间时取消下载。

  • -Y/–speed-limit speed -Y指定最低速度而未指定超时时间时,超时时间默认为30。

  • -z/–time-cond date expression ​只在更新时下载。

具体的时间格式请参考man 3 curl_getdate(这个只有在安装了libcurl开发库之后才有),或者 curl_getdate在线文档。如果时间格式解析失败,curl将会把此字段理解为文件名,从文件中读取时间。需要网页中有相关字段表明最后更新日期。

  • 文件批量下载

    curl -O http://10.1.1.3/{a,b}/[1-7].php

a/1.php-a/7.php会保存在当前目录下的1.php-7.php中。b/1.php-b/5.php如果也保存在当前目录下的 1.php-5.php的话,会覆盖以前的文件。因此curl会把b/1.php-b/5.php的内容在终端输出而不会保存文件。而执行:

curl "http://10.1.1.103/{a,b}/[1-7].php" -o "#1_#2.php"

则会使用“目录_文件”的形式命名文件,避免文件名冲突。

{}[]类似于正则表达式中的语法,用以取得多个文件。#加数字的形式用以取得正则表达式的当前值。如 #1 表示第一个正则表达式的当前值,上例中即为{a,b}的值。

当下载http://10.1.1.103/a/1.php时,#1为“a”,#2为“1”,#1_#2.php即为“a_1.php”。从而完成批量文件的自动命名。

注意此时网址包含在双引号中,这是因为如果不被包含在引号中,{}中的内容无法使用#1的形式引用,会出现如下错误

internal error: invalid pattern type (0) 
Warning: bad output glob!
  • -T, –upload-file file

    使用HTTP的PUT方法上传文件,需要目标URL支持HTTP的PUT方法。可以与-d、–data-urlencode、-F同时使用。

    上传一个文件: curl -u ftpuser:ftppass -T myfile.txt ftp://ftp.testserver.com

    上传多个文件: curl -u ftpuser:ftppass -T “{file1,file2}” ftp://ftp.testserver.com

    从标准输入获得文件内容: curl -u ftpuser:ftppass -T - ftp://ftp.testserver.com/myfile_1.txt 这个命令会将标准输入的内容保存在ftp://ftp.testserver.com/myfile_1.txt中。

  • -L, –location 有的网址是自动跳转的,curl就会跳转到新的网址。

    curl -L www.sina.com

  • -i, –include 参数可以显示http response的头信息,连同网页代码一起。

    curl -I www.sina.com

    HTTP/1.1 301 Moved Permanently Server: nginx Date: Tue, 19 Aug 2014 15:44:21 GMT Content-Type: text/html Location: http://www.sina.com.cn/ Expires: Tue, 19 Aug 2014 15:46:21 GMT Cache-Control: max-age=120 Age: 4 Content-Length: 178 X-Cache: HIT from xd33-95.sina.com.cn

      <html>
      <head><title>301 Moved Permanently</title></head>
      <body bgcolor="white">
      <center><h1>301 Moved Permanently</h1></center>
      <hr><center>nginx</center>
      </body>
      </html>
    
  • -I, –head

    curl -I www.sina.com

    HTTP/1.1 301 Moved Permanently Server: nginx Date: Tue, 19 Aug 2014 15:44:38 GMT Content-Type: text/html Location: http://www.sina.com.cn/ Expires: Tue, 19 Aug 2014 15:46:38 GMT Cache-Control: max-age=120 Age: 31 Content-Length: 178 X-Cache: HIT from xd33-90.sina.com.cn

  • -v/–verbose 查看通信过程、调试。

    这个参数可以清晰的查看到curl的通信过程,以便于调试,如执行:

    curl -v http://www.sina.com 可以清晰的看到curl发出的HTTP请求及服务器返回的HTTP响应

以下所有命令都可以通过加入-v选项,查看具体通信过程。 更详细的调试信息可以使用–trace-ascii选项,如:

curl --trace-ascii debug.txt http://www.sina.com
curl --trace output.txt www.sina.com

####GET一个网页

curl http://www.example.com  网页源码将被打印出来。
  • -o/–output file 指定保存目录便于后续处理,我们将网页内容保存在文件中:

    curl -o example.html http://www.example.com 此时仍会显示进度条等信息。

    curl -o example.html http://www.example.com -o example1.html http://www.example1.com 可同时下载多个URL,每个-o对应一个URL

  • -s/–silent 静默模式

通过脚本结合curl可以实现很复杂的功能,而在脚本中调用curl,我们不需要进度条等信息。如执行:

curl -s -o adeploy.html http://www.adeploy.com 将不会显示任何信息。
  • -A, –user-agent agent string 指定User-Agent字段

此选项字段用于指定HTTP请求头的User-Agent字段,即客户端(如浏览器)类型。很多网站为了防止自动化程序采集,仅允许浏览器访问,最简单的办法就是通过User-Agent字段识别客户端类型做出处理。而如下命令:

curl -A “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)” http://www.adeploy.com

“Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)”为firefox的UserAgent值。

通过-A指定User-Agent字段,使curl伪装成Firefox,从而绕过网站的检测。

User-Agent字段可以通过抓包分析得到,或者通过chrome的Developer Tools、Firefox的Firebug等工具得到。 而如果一个网站对浏览器的限制也很严格的话,那我们甚至可以通过修改User-Agent伪装成百度蜘蛛或者googlebot 突破限制,因为基于SEO的考虑,网站对搜索引擎蜘蛛的限制很小。

  • -e/–referer URL 设置Referer

此选项字段用于指定 HTTP 请求头中的 Referer 字段,即来源网页。一个服务器端常用的限制方法, 就是检查http访问的referer。比如你先访问首页,再访问里面所指定的下载页,这第二次访问的referer 地址就是第一次访问成功后的页面地址。这样,服务器端只要发现对下载页面某次访问的referer地址不是 首页的地址,就可以断定那是个盗连了~~~~~

为了防盗链,很多网页尤其是图片等可下载资源会检测Referer字段,对于非自己站内的来源全部屏蔽掉,此时我们就需要这个参数。 通过执行:

curl -A  "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -e http://www.baidu.com http://www.adeploy.com

####POST

-X, –request command

curl -X POST www.example.com

curl -d "firstName=Kris"  -d "lastName=Jordan" www.example.com


curl -X PUT -H 'Content-Type: application/json' -d '{"firstName":"Kris", "lastName":"Jordan"}' www.example.com

curl -X PUT  -H 'Content-Type: application/json' -d @example.json  www.example.com

-d, –data data

指定HTTP请求时发送的数据(主要为POST请求),使用和用户通过浏览器提交表单时一样的方式。
使用的content-type是application/x-www-form-urlencoded。
相当于-data-ascii。发送纯粹二进制的数据(data purely binary),需要使用–data-binary。
URL-encode编码要发送的数据,需要使用–data-urlencode。

当有多个-d时,curl会自动将发送的数据段是用“&”符号拼接,如:-d name=Adeploy -d age=100
将自动拼接为name=Adeploy&age=100作为post数据块(post chunk)发送。

如果想指定文件中的内容,可以使用-d @filename的形式。如文件foobar中内容为name=Adeploy,
使用-d @foobar即可达到-d name=Adeploy一样的效果(只有第一个=作为特殊字符)。从文件读入
时要注意=、@等特殊字符的异常情况。

而-d @-可以指定要发送的内容来自标准输入(stdin)。如执行curl -d @- http://127.0.0.1/welcome.php,
在终端输入数据后回车、Ctrl+D后达到相同效果。

对于文件的使用,对于其他类似选项适用。

但-d/–data不会对数据进行url编码,而实际场景中我们最常用的还是发送url编码后的数据。

curl --data "data=xxx" example.com/form.cgi	

–data-urlencode data

使用URL-encode编码要发送的数据。除此之外,其余同-d。-d选项默认是不会对要发送数据进行编码的,
在旧版本没有–data-urlencode选项的curl时,要发送编码的数据,必须手动对数据进行编码,或者将数
据存放在编码的文件中。比如要发送数据为”Adeploy blog”,必须手动把空格变为”%20″:

curl -d name=Adeploy%20blog -d age=100 http://127.0.0.1/welcome.php 

而–data-urlencode自动完成此过程:

curl –data-urlencode name=Adeploy blog –data-urlencode age=100 http://127.0.0.1/welcome.php

curl --data-urlencode "date=April 1" example.com/form.cgi

在我们的程序中,直接使用-d发送需urlencode编码的数据(一般为除字母数字外的其他字符,如空格,汉字等),
程序也会正常处理,那我们为什么还需要使用–data-urlencode呢?
原因如下:
1)能正常处理是apache服务器的原因。实际使用中服务器、程序语言不同,很可能出现无法处理不urlencode编码
的数据,尤其是汉字和jsp程序,容易出现乱码

2)我们在使用浏览器操作时,浏览器会自动进行urlencode操作。为了尽可能逼真,我们使用–data-urlencode尽
可能模仿浏览器的操作

–data-urlencode同样可以使用@指定从文件中输入。而且可以使用name@filename的形式。如使用–data-urlencode
 name@foobar,文件foobar中内容为Adeploy,即可达到–data-urlencode name=Adeploy的效果。这在-d选项中是
 不行的。需要注意的是此用法不会对name进行编码,所以需要预先编码好name字段。实际场景中一般name字段都是
 固定的,so it’s not too much trouble.

–data-binary data

发送指定的不做任何处理的数据(This posts data exactly as specified with no extra processing whatsoever),其余同-d。

-F, –form name=content

1. 一般使用-F file=@filename的形式,其中file为文件表单的name,filename为文件名。@后面的文件作为文件上传处理。

curl --form upload=@localfilename --form press=OK [URL]

	<form method="POST" enctype='multipart/form-data' action="upload.cgi">
	    <input type=file name=upload>
	    <input type=submit name=press value="OK">
	  </form>

curl -F "firstName=Kris" -F "publicKey=@idrsa.pub;type=text/plain" www.example.com

	{
	  "method": "POST",
	  ...
	  "headers": {
		"content-length": "697",
		"content-type": "multipart/form-data;
		boundary=----------------------------488327019409",
		... },
	  "body": "------------------------------488327019409\r\n
			   Content-Disposition: form-data;
			   name=\"firstName\"\r\n\r\n
			   Kris\r\n
			   ------------------------------488327019409\r\n
			   Content-Disposition: form-data;
			   name=\"publicKey\";
			   filename=\"id_rsa.pub\"\r\n
			   Content-Type: text/plain\r\n\r\n
			   ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAkq1lZYUOJH2
			   ... more [a-zA-Z0-9]* ...
			   naZXJw== krisjordan@gmail.com\n\r\n
			   ------------------------------488327019409
			   --\r\n",
	}
	
	
2. 对于 &lt 后的文件,curl 会读取其内容,而不会作为文件上传。类似 -d 选项中 @ 的作用。
	
		curl -F name=<foobar.txt -Fage=100 http://127.0.0.1/welcome.php

foobar.txt内容为Adeploy,则此命令与以下命令等价:

		curl -F name=Adeploy -Fage=100 http://127.0.0.1/welcome.php

而-F @foobar.txt表示将foobar.txt作为文件上传。

3. -F不进行url编码

-F与-d类似,都是不对数据进行urlencode编码,而且-F不能与-d或–data-urlencode共用。
因此如果上传文件的同时还需要提交其他需要urlencode编码的字段(如中文用户名),就会出现问题。
除手动编码外,实用的解决办法是自己编写urlencode函数(网上可以找到Linux脚本实现的urlencode
函数或C语言版本、Perl版本、Python),调用即可。

4. 使用\"type=\"指定Content-Type
如curl -F “web=@index.html;type=text/html” url.com
将index.html以text/html的Content-Type上传。

-form-string name=string

基本同-F,但是此参数后面跟”@”、”<”、”;type=”时,不做特殊含义解析。

-H, –header header

			curl -H "Accept: application/json" -H "Authorization: OAuth 2c3455d1aeffc" http://sina.com

-G/–get

和-d/–data、–data-binary一块使用时,表示强制使用GET的方式提交表单。
和 -I/–head 一块使用时,表示把提交的数据加在url中,而不是加在数据头中。

###cookie使用

cookie的形式就是 “属性:值” 对形式,-b 后可直接以“属性=值”的形式给出cookie,当有多个属性时以分号间隔且包含在双引号中。如执行:

curl -A  "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -b "user=Adeploy;pass=password" http://www.adeploy.com

会将 cookie 信息 user=Adeploy;pass=password 附加在HTTP请求头中。

我们登录网站后,为保持登录状态,需要使用cookie信息。

  • -D/–dump-header file 保存协议头部信息

头部信息中包含最常使用的cookie信息,如执行:

curl -A  "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -e http://www.baidu.com -D header.txt http://www.adeploy.com

会将头部信息保存至 adeploycookie.txt 中。

  • -b, –cookie name=data 指定cookie

-b 后可直接加 -D 保存的文件,curl 会自动从中读取出 cookie 值,而且 -b 选项不会修改此文件。如执行:

curl -A  "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -e http://www.baidu.com -b header.txt http://www.adeploy.com

会自动将adeploycookie.txt中cookie信息附加至HTTP请求头中。因此,结合-D和-b参数,即可完成cookie的保存和后续使用。

###Redirects重定向

打开某些网页,网页会显示“ 3XX Moved Permanently ”,即网页被移动到其他位置,新位置一般由HTTP响应头中的 Location 字段给出。 重定向一般是因为网站结构调整后,避免用户访问原来页面出现404错误。 而另一类常见的用法就是在登陆成功后,网页显示成功信息,然后将用户重定向至内容页面。

  • -L/–location 选项可以自动跟踪重定向

####认证相关选项

-E/--cert <certificate[:password]>

--cert-type <type>

--cacert <CA certificate>

--capath <CA certificate directory>

-k/--insecure

--key <key>

--key-type <type>

--proxy-anyauth
--proxy-basic
--proxy-digest
--proxy-negotiate
--proxy-ntlm

####代理选项

--socks4a <host[:port]>
--socks4 <host[:port]>
--socks5 <host[:port]>
--socks5-hostname <host[:port]>
--socks5-gssapi-service <servicename>
--socks5-gssapi-nec

-x/--proxy <proxyhost[:port]>
-U/--proxy-user <user:password>

curl -x proxy.xxx.com:8080 -U username:password -e http://www.ip138.com/ http://iframe.ip138.com/city.asp
curl -x 'http://username:password@proxy.xxx.com' -e http://www.ip138.com/ http://iframe.ip138.com/city.asp

###wget VS cURL

####相同点

​* 可通过FTP/HTTP/HTTPS协议下载文件的命令行工具;

  • 可以发送HTTP POST请求;
  • 支持cookie的使用;
  • 被设计为无界面(UI),可在如脚本中使用;
  • 开源且免费的软件。

####不同点

curl

  • curl是基于libcurl库实现,因此特性来源于libcurl库。libcurl库跨平台、开源且免费,因此curl不只是一个命令行工具
  • 管道。curl在传统的UNIX风格的基础上加以扩展,对输入输出做出更多的处理,从而贯彻了”everything is a pipe” 的设计思想。
  • 单点传送(Single shot)。curl被设计为用于数据的单点传送。它只会传送用户指定的URLS,并不包含任何递归下载的逻辑,也不对html进行任何解析。
  • 更多协议支持。目前curl 支持的协议有: FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS, FILE, POP3, IMAP, SMTP 和 RTSP 。Wget仅仅支持 HTTP, HTTPS 和 FTP。(协议的支持情况可能会随着时间发展而变化。)
  • 更多平台支持,便于移植。与wget相比,curl可在更多平台上编译和运行。比如OS/400, TPF 和其他很多非UNIX的平台。
  • 更强大的SSL库和SSL支持。curl能应用于多个不同的 SSL/TLS 库, 并提供了更多的可控性和对协议细节更广泛的支持。
  • curl支持更多的HTTP认证方式。尤其是当你使用HTTP代理的时候,curl支持Basic, Digest, NTLM and Negotiate认证方式(参见HTTP认证模式)。
  • libcurl库支持多种不同的SOCKS代理,基于libcurl实现的curl工具也支持。而wget不支持。
  • 双向通信。 curl 提供了上传能力,Wget仅仅支持HTTP POST方式。
  • HTTP multipart/form-data 的发送能力。 这使得用户可以进行HTTP上传,更好的模拟浏览器的行为从而更大程度的实现HTTP操作的自动化,提供了更多操作的可能。

  • 压缩。 curl 支持GZIP和 inflate Content-Encoding 并且自动进行解压缩操作。
  • curl提供并执行Transfer-Encoded HTTP的解压缩,而wget不会
  • curl在7.27.0之后,提供对metalink的支持(好吧,百度百科对metalink的解释是不对的, 百度百科说的是oracle公司的一个服务)。而wget需要依靠一个Google Summer开源项目才能支持metalink。

wget

  • wget仅仅是一个命令行工具。没有库提供使用。
  • 递归下载支持。wget相比curl最大的优点在于对递归下载的支持,甚至下载一个资源如网页或FTP目录指向的所有资源。
  • 下载时无需指定保存文件名。而curl需要使用-o或-O指定。
  • 仅支持GnuTLS 和 OpenSSL两种SSL库。
  • 仅支持HTTP代理的基本认证(Basic auth)。
  • 不支持SOCKS代理。
  • 项目更老,基于GPL开源协议,隶属GNU项目。curl基于MIT开源协议,不隶属于任何组织,版权由作者一人所有。

参考

http://curl.haxx.se/docs/httpscripting.html