curl是一个命令行工具,通过指定的URL来上传或下载数据,并将数据展示出来。curl中的c
表示client,而URL,就是URL。这里我们介绍一下curl的使用。
1. 命令行基础
1.1 命令行选项
1. 短形式
在curl中可以使用短形式的命令行选项,比如通知curl打开verbose模式,可以使用-v
选项:
$ curl -v www.baidu.com
这里,-v
就是短形式的选项,我们使用一个中划线(-)并紧跟着一个字母来指定一个短形式的选项。
在这个例子中,-v
就像一个开关一样,指定某个变量是false
还是true
。我们可以在一个中划线后面跟多个单字母的选项:
$ curl -vL www.baidu.com
在curl中,命令行解析器总是解析整个命令行,因此,选项可以放在整个命令行的任何位置:
$ curl www.baidu.com -vL
这个和上面的命令具有同样的效果。当然,虽然是任何位置,但不能放在curl
前面啊:
$ -vL curl www.baidu.com // No command '-vL' found
2. 长形式
单个字母的选项敲起来和用起来都很方便,但是字母的个数有限而需要控制的东西又太多,这个时候就可以使用选项的长形式。同样,为了使得命令便于阅读,绝大多数短形式都有一个对应的长形式。
和短形式不同的是,长形式的选项使用两个中划线(--)指定,然后紧跟着具体的选项。还有就是,在使用长形式的时候,—
后面只能跟一个选项。对于-v
,对应的长形式如下:
$ curl --verbose www.baidu.com
同样,长形式的选项也可以出现在命令的任何地方:
$ curl www.baidu.com --verbose
对于-vL
来说,对应的长形式可以是:
$ curl --verbose --location www.baidu.com
或者:
$ curl --location www.baidu.com --verbose
3. 选项的参数
在上面的命令中选项-v
(或--verbose
)以及-L
(或--location
)都是bool
类型的标志位,来告诉curl打开或关闭某些特征。curl还有一种类型的选项,就是需要传递一些参数。比如,如果想在一个HTTP POST中向服务器传递一个字符串:
$ curl -d arbitrary http://example.com
同样,也可以使用相应的长形式:
$ curl --data arbitrary http://example.com
4. 参数有空格?
在上面的例子中,我们的参数arbitrary
是一个连续的字符串,但是当我们需要传递一个有空格的参数怎么办?比如Are you OK?
,这时我们需要使用引号把参数括起来:
$ curl -A "Are you OK?" http://example.com
如果不加引号的话:
$ curl -A Are you OK? http://example.com
那么curl只会把Are
当做用户的参数,剩下的字符,you
和OK?
会被curl当做额外的URL处理,因为这里并没有用-
指定这是一个选项。
但是如果参数本身有引号的时候怎么办?这在使用JSON传递参数的时候尤其常见,我们可以使用单引号把参数括起来(不过在Windows中不管用):
$ curl -d '{"name":"fool"}' http://example.com
当数据很多时,我们可以指定一个文件,来传递给curl:
$ curl -d @params.json http://example.com
5. Say No
对于像-t
和-L
之类的标志选项,我们可以在长形式的前面加上no-
前缀来指定关闭相应的特征,比如关闭verbose
模式:
$ curl --no-verbose http://example.com
1.2 URL
curl支持在一个命令行中处理多个URL,中间用空格间隔即可。curl会对传进来的URL做简单的验证,而不会去验证URL是否真正有效,因此,这里需要使用者提供有效的URL。
前面说过,curl首先解析整个命令行,将得到选项应用于所有的URL上。如果想对每一个URL使用不同的选项,那么可以使用--next
来指定。比如:
$ curl --location http://example.com/1
--next --data sendthis http://example.com/2
--next head http://example.com/3
1. 配置文件
如果选项过多,导致命令很难输入,或者超过了系统命令最大长度的限制,我们可以使用配置文件(config file)来指定curl的选项。
通过使用-K
或--config
选项来告诉curl从指定的文件中读取选项,比如:
$ curl -K curl.options http://example.com
在文件curl.options
中,列出所有需要的选项:
# ask curl to follow redirects
--location
# ask curl to do a HEAD request
--head
和在命令行中一样,在配置文件中也可以使用长形式或短形式,甚至在配置文件中对于长形式可以省略那两个中划线(--):
# ask curl to follow redirects
location
# ask curl to do a HEAD request
head
对于使用参数的选项,同样可以使用配置文件:
# ask curl to change the User-Agent in HTTP header
user-agent "something-is-an-agent"
既然叫做配置文件,那么上面的选项也可以写作:
# ask curl to change the User-Agent in HTTP header
user-agent = "something-is-an-agent"
甚至可以省略没有空格的参数的引号:
# ask curl to change the User-Agent in HTTP header
user-agent = something-is-an-agent
当然,如果参数中有空格的话就不能省略引号了。
2. 开始使用curl
在前面我们简单地介绍了什么是curl以及一些基础的命令行知识。我们通过命令行的方式将需要处理的URL交给curl去处理。
在这里,我们开始着手使用curl,了解curl能做什么以及如何去做。
2.1 Verbose模式
如果curl得到的结果不是期望的结果,我们可以使用-v
或--verbose
进入Verbose模式获取更多的信息。
1. 查看通信过程
在Verbose模式中,curl会得到更多的对话式信息,帮助我们了解发生了什么。curl会在每一个信息前面加上*进行标识。在下面的例子中,我们将百度的首页保存下来(使用-o
选项并指定参数baidu
):
$ curl -v www.baidu.com -o baidu
我们可以得到如下的信息:
* About to connect() to www.baidu.com port 80 (#0)
* Trying 14.215.177.39... connected
* Connected to www.baidu.com (14.215.177.39) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: www.baidu.com
> Accept: */*
>
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2381
< Content-Type: text/html
< Date: Fri, 14 Sep 2018 09:55:18 GMT
< Etag: "588604dd-94d"
< Last-Modified: Mon, 23 Jan 2017 13:27:57 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<
{ [data not shown]
103 2381 103 2381 0 0 113k 0 --:--:-- --:--:-- --:--:-- 232k* Connection #0 to host www.baidu.com left intact
* Closing connection #0
下面的信息是建立一个链接:
* About to connect() to www.baidu.com port 80 (#0)
* Trying 14.215.177.39... connected
* Connected to www.baidu.com (14.215.177.39) port 80 (#0)
然后就是本次的HTTP请求:
> GET / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: www.baidu.com
> Accept: */*
>
接下来是数据的传输过程。然后就是响应:
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2381
< Content-Type: text/html
< Date: Fri, 14 Sep 2018 09:55:18 GMT
< Etag: "588604dd-94d"
< Last-Modified: Mon, 23 Jan 2017 13:27:57 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<
连箭头都不一样了。
2. 更详细的信息
如果觉得使用-v
的信息还不够的话,还可以使用--trace [filename]
选项来讲完整的流保存到filename
中。比如:
$ curl --trace dump www.baidu.com
之后,就可以发现一个新的文件dump
,里面保存着刚才那个会话的所有信息:
== Info: About to connect() to www.baidu.com port 80 (#0)
== Info: Trying 14.215.177.39... == Info: connected
== Info: Connected to www.baidu.com (14.215.177.39) port 80 (#0)
=> Send header, 166 bytes (0xa6)
0000: 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1..
0010: 55 73 65 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c User-Agent: curl
0020: 2f 37 2e 31 39 2e 37 20 28 78 38 36 5f 36 34 2d /7.19.7 (x86_64-
0030: 72 65 64 68 61 74 2d 6c 69 6e 75 78 2d 67 6e 75 redhat-linux-gnu
0040: 29 20 6c 69 62 63 75 72 6c 2f 37 2e 31 39 2e 37 ) libcurl/7.19.7
0050: 20 4e 53 53 2f 33 2e 32 37 2e 31 20 7a 6c 69 62 NSS/3.27.1 zlib
0060: 2f 31 2e 32 2e 33 20 6c 69 62 69 64 6e 2f 31 2e /1.2.3 libidn/1.
0070: 31 38 20 6c 69 62 73 73 68 32 2f 31 2e 34 2e 32 18 libssh2/1.4.2
0080: 0d 0a 48 6f 73 74 3a 20 77 77 77 2e 62 61 69 64 ..Host: www.baid
0090: 75 2e 63 6f 6d 0d 0a 41 63 63 65 70 74 3a 20 2a u.com..Accept: *
00a0: 2f 2a 0d 0a 0d 0a /*....
<= Recv header, 17 bytes (0x11)
0000: 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d HTTP/1.1 200 OK.
0010: 0a .
<= Recv header, 22 bytes (0x16)
0000: 41 63 63 65 70 74 2d 52 61 6e 67 65 73 3a 20 62 Accept-Ranges: b
0010: 79 74 65 73 0d 0a ytes..
文件的前21行如上所示。每一个发送和接收的数据以十六进制的形式保存起来了,方便以后的分析。
如果觉得十六进制没啥帮助的话,可以使用--trace-ascii [filename]
选项:
$ curl --trace-ascii dump www.baidu.com
结果如下:
== Info: About to connect() to www.baidu.com port 80 (#0)
== Info: Trying 14.215.177.38... == Info: connected
== Info: Connected to www.baidu.com (14.215.177.38) port 80 (#0)
=> Send header, 166 bytes (0xa6)
0000: GET / HTTP/1.1
0010: User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7
0050: NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
0082: Host: www.baidu.com
0097: Accept: */*
00a4:
<= Recv header, 17 bytes (0x11)
0000: HTTP/1.1 200 OK
<= Recv header, 22 bytes (0x16)
0000: Accept-Ranges: bytes
<= Recv header, 76 bytes (0x4c)
0000: Cache-Control: private, no-cache, no-store, proxy-revalidate, no
0040: -transform
<= Recv header, 24 bytes (0x18)
0000: Connection: Keep-Alive
<= Recv header, 22 bytes (0x16)
0000: Content-Length: 2381
上面就是前21行的输出。
3. Silence
verbose模式的对立模式,就是silence,可以使用-s
或--silence
选项来告诉curl不输出任何程序的信息或者错误信息,但也会输出响应的结果。
如果需要在有错误的时候输出错误信息,可以使用-S
或--show-error
来指定。
2.2 浏览器到curl
别人使用浏览器发起了一个请求之后,如果自己想用curl再来一次同样的请求,这里日常的工作中是比较常见的一个操作。在curl中,有没有什么比较简便快捷的方式来获得curl命令呢?
Chrome浏览器和Firefox浏览器都实现了复制成curl的工具,可以将浏览器的请求快速复制成curl命令,非常方便快捷。
1. Chrome
在Chrome中,打开More tools->Developer模式,选择Network选项卡,然后就可以看到所有的请求,选中相应的请求,右键就有Copy as cURL
选项,单击就可以了。
2. Firefox
在Firefox中,打开Web Developer->Network工具,然后右键想要复制的链接,就有一个Copy as cURL
的选项,单击就可以了。
3. HTTP与curl
与curl一起使用的协议,最多的还是HTTP,这里就将介绍如何有效地使用curl来发送HTTP请求。
3.1 HTTP方法
在每一个HTTP请求中,都有一个对应的方法,常用的方法有:GET
、POST
、HEAD
和PUT
。
如果在一个curl命令中不指定具体的方法,那么默认的就是使用GET
方法。对于其它的方法,可以在curl命令中指定:
method | option |
---|---|
POST |
-d 或-F
|
HEAD |
-I |
PUT |
-T |
3.2 Header
在curl中,使用-i
选项可以显示Response的Header信息,连同Body数据:
$ curl -i www.baidu.com
结果:
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Connection: Keep-Alive
Content-Length: 2381
Content-Type: text/html
Date: Mon, 17 Sep 2018 10:26:42 GMT
Etag: "588604dd-94d"
Last-Modified: Mon, 23 Jan 2017 13:27:57 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta
http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=styleshee
t type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就
知道</title></head> <body link=#0000cc> ... </body> </html>
使用-I
选项可以只显示Response的Header信息:
$ curl -I www.baidu.com
3.3 POST
POST
是HTTP中向服务端提交数据的一种方法。在浏览器中,但在表单中填写完数据后,浏览器就会默认将填写的数据使用key=value
串的形式进行转化。在curl中,我们可以使用-d
或--data
选项来指定具体的数据:
$ curl -d key1=value1&key2=value2 http://example.com
我们也可以使用多个-d
选项来指定多组数据,curl会自动把这些数据连接起来,因此上面的例子还可以这样:
$ curl -d key1=value1 -d key2=value2 http://example.com
当然,如果数据过多,我们还可以把数据放在一个文件中:
$ curl -d @filename http://example.com
1. Content-Type
当使用POST
方法提交数据时,对于提交的数据主要有如下四种形式:
-
application/x-www-form-urlencoded
:默认的形式,即key1=value1&key2=value2
的形式; -
multipart/form-data
:使用表单上传文件时使用这个形式; -
application/json
:提交JSON格式的数据; -
text/xml
:提交XML格式的数据。
Content-Type是一个Header,如果不指定的话,那么默认就是使用application/x-www-form-urlencoded
形式传输数据,当需要使用别的形式进行数据传输的话,那么就需要指定这个Header:
$ curl -d '{I Am A JSON FORM}' -H 'Content-Type: application/json' http://example.com
其中,-H
就是用来指定一个具体的Header的选项,值就是key=value
的形式。当需要指定其它的Header,可以使用-H
选项。
2. POST一个二进制数据
在curl中,我们也可以提交一个文件,可以使用--data-binary
选项来指定一个文件:
$ curl --data-binary @filename http://example.com
3. 转化成一个GET
使用-G
或-get
选项,可以把一个POST请求转化成一个GET请求。如果有-d
选项指定的参数,那么curl就会把-d
后面的数据添加到URL的后面,用?
连接。比如:
$ curl -d "key1=value1" -G http://example.com
得到的请求URL就是:
http://example.com/?key1=value1
4. URL编码
如果使用的数据没有编码,那么可以指定curl来帮助自己进行编码。这时可以使用--data-urlencode
选项来指定。比如:
$ curl --data-urlencode "name=Alan Walker" http://example.com
5. multipart formposts
如果一个HTTP POST具有如下形式的表单:
<form action="submit.cgi" method="post" enctype="multipart/form-data">
Name: <input type="text" name="person"><br>
File: <input type="file" name="secret"><br>
<input type="submit" value="Submit">
</form>
用户可以在Name
中填写名字,在File
中选择一个文件,然后单击Submit
按钮提交数据。
为了可以在curl中模拟这个请求,我们可以使用-F
或--form
选项来指定数据:
$ curl -F person=annonymous -F secret=@filename http://example.com/submit.cgi
在上面的表单中,action
指定了这个请求发送到哪里;method
指定这是一个POST
请求;而enctype
指定了这是一个multipart formpost。
当执行上面的curl命令后,curl会产生如下的请求头:
POST /submit.cgi HTTP/1.1
Host: example.com
User-Agent: curl/7.46.0
Accept: */*
Content-Length: 313
Content-Type: multipart/form-data; boundary=------------------------d74496d66958873e
其中Content-Type
是和enctype
一致的。
当使用-F
选项时,默认的Content-Type
就是multipart/form-data
,不过,我们也可以使用-H
进行指定:
$ curl -F 'name=Dan' -H 'Content-Type: multipart/magic' https://example.com
6. -d
vs -F
在前面我们介绍了使用-d
构造一个基本的POST请求,和-F
构造一个multipart formpost请求。那么这两个选项有啥区别以及什么时候使用呢?
这两个选项都是把指定的数据发送到服务器上,区别在于数据传递的格式。大多数时候,接收端来指定希望客户端发送数据的格式,客户端不能随意自己指定格式。
- HTML表单
当使用HTML表单时,会使用<form>
标签指定一个表单,这会让浏览器使用POST
方法。如果标签中含有enctype=multipart/form-data
,这意味着使用multipart formpost方式,在curl中就是使用-F
选项。一个典型的场景就是表单中含有<input type=file>
标签。
- 不用HTML表单
POST
方法不一定非要在HTML中,在好多的service、APIs中,也可以使用POST
请求。
如果这些service期望使用JSON或者其它类似的格式的数据,那么这就是一个普通的POST请求。在curl中就可以使用-d
选项。不过要注意-d
的默认Content-Type是不是期望的格式,如果不是的话,可以使用-H
进行更改。
3.4 HTTP重定向(redirect)
重定向是HTTP协议中的一个基础部分。在重定向中,服务器给客户端的并不是客户端想要的内容,而是一个车具体的指令,告诉客户端如果想获取想要的数据,应该到哪里去请求。
但不是所有的重定向都一样。重定向之后的请求使用什么方法呢?重定向多久呢?
所有的重定向都会返回Location:
的Header,来指定一个新的URL。
1. curl:redirect
在curl中,默认不会重定向,可以使用-L
或--location
选项来告诉curl重定向:
$ curl -L http://example.com
2. GET
还是POST
第一次请求后,服务器会告诉客户端下一次请求需要使用的方法。关于重定向的响应码如下:
Method | Permanent | Temporary |
---|---|---|
切换到GET
|
301 | 302和303 |
使用第一次请求的方法 | 308 | 307 |
我们可以指定curl在重定向时使用什么方法。如果我们第一次请求使用的不是GET
方法,重定向后也不希望curl默认使用GET
方法,那么我们可以使用--post301
,--post302
和--post303
选项来指定。
3.5 修改HTTP请求
每一个请求都有一个请求行、一些请求头和可选的请求体,这里我们看看在curl中可以具体修改的部分,包括请求行和请求头。
1. 请求方法
在请求行中包含这次请求所使用的方法。我们使用下面的简单命令就可以进行一个GET
方法:
$ curl http://example.com/file
这会生成如下的请求行:
GET /file HTTP/1.1
在HTTP方法中我们可以通过具体的选项指定使用什么方法。这里我们也可以使用-X
选项来进行指定:
$ curl -X POST http://example.com
2. 修改请求头
在curl中,我们可以使用-H
或--header
选项来指定Header。之前我们就使用-H
指定了Content-Type
,其实Header就是一个key: value
对:
$ curl -H "HeaderName: HeaderValue" http://example.com
3. Referer
我们还可以在curl通过--referer
选项来指定我们是从哪里跳转过来的:
$ curl --referer http://fromexample.com http://toexample.com
4. User Agent
这个字段是用来表示客户端的设备信息的,服务器会根据这个字段,针对不同的设备,返回不同格式的网页。在curl中,可以使用--user-agent
选的来指定:
$ curl --user-agent "[User Agent]" http://example.com
3.6 Cookies
HTTP是一种无状态的协议,为了在会话中保存一些状态,可以使用Cookies。服务器通过Set-Cookie:
来设置Cookie,客户端就可以在下一次请求中携带这些数据。
1. 设置Cookie
我们可以使用--cookie
选项来设置一个Cookie:
$ curl --cookie "CookieName=CookieValue" http://example.com
2. 从文件中读取Cookies
curl默认不会记住服务器设置的Cookie,也不会在下一次请求中携带Cookie。除非用户通过选项自己设置。
我们可以把之前的Cookies保存到一个文件,然后在下一次请求中指定curl读取文件中的Cookies:
$ curl -b cookies.txt http://example.com
-b
选项指定curl去给定的文件中读取Cookies。
不过要主要,这里仅仅是读取Cookies,如果这次请求中服务器修改了Cookie,那么curl是不会进行保存的,除非我们手动指定。
3. 写Cookies到文件
我们可以使用-c
选项指定curl保存这次请求中服务器设置的Cookies:
$ curl -c cookie.jar.txt http://example.com
有时,我们既需要从文件中读取Cookies,也需要保存服务器设置的Cookies。那么就可以同时使用-b
和-c
选项:
$ curl -b cookies.txt -c cookie.jar.txt http://example.com