Linux下cURL使用教程之五:curl基本使用之提交表单

很多时候,我们想获得的内容或者想进行的操作,只有在登陆之后才有权限,此时就需要用到提交表单。
表单以HTML的形式呈现给用户,用户使用表单提交数据后,服务端获得表单数据进行处理,然后进行后续操作。比如我们登录的过程,登录的表单呈现给我们,输入用户名密码提交后,服务端获得用户名密码在数据库中查询,以查询结果判定密码正确与否。
向HTTP服务器提交数据常用的有GET和POST两种方法。下文针对这两种方法,结合实例说明curl的使用。

PHP程序

同前篇,服务端所有代码通过php实现。

  • post.php,显示表单用于POST提交数据:
    <html>
    <head>
    <title>
    PHP POST Demo for curl basic usage By Stackeye
    </title>
    </head>
    <body>
    This is a post form demo.<br /><br />
    <form action="welcome.php" method="post">
    Name: <input type="text" name="name" />
    Age: <input type="text" name="age" />
    <input type="submit" />
    </form>
    <br /><br /><br />
    <a href="http://www.stackeye.com" target="_blank">Stackeye's Blog</a>
    </body>
    </html>
  • welcome.php,处理POST或GET的数据并显示:
    <html>
    <head>
    <title>
    PHP Form Demo for curl basic usage By Stackeye
    </title>
    </head>
    <body>
    This is a PHP Form Demo.<br />
    It can parse both GET and POST data.<br />
    This is a <strong>
    <?php
    if(count($_POST) > 0)
    echo "POST";
    else
    echo "GET";
    ?></strong> Request.<br /><br />
    Welcome <strong><?php echo $_REQUEST["name"]; ?></strong>.<br />
    You are <strong><?php echo $_REQUEST["age"]; ?></strong> years old.
    <br /><br /><br />
    <a href="http://www.stackeye.com" target="_blank">Stackeye's Blog</a>
    </body>
    </html>

跟踪表单提交的过程

提交表单的过程比GET网页的过程要复杂些。GET一个网页一般只会对Referer、User-Agent等字段进行限制。而提交表单的过程,需要知道提交的数据的字段名称、字段值的格式等。而且某些表单还含有隐藏字段。因此使用curl提交表单一般需要我们分析网页源代码,甚至采用抓包工具等进行数据包的分析。

我们分析的一般都是HTTP协议,chrome自带的Developer Tools可以更容易的分析HTTP协议,因此我们使用此工具(Firefox上的Firebug插件提供类似功能)。
以下分析基于chrome浏览器。Firefox浏览器需要自行安装firebug插件。

使用chrome打开网页(注意360急速浏览器下要使用“极速模式”,不要使用“兼容模式”),然后右键“审核元素”即可,如下图:
chrome developer tools
几个比较重要的tab页:

  • Element标签用于显示当前元素,如想看到一个按钮的源码,对按钮右键审核元素即可看到。
  • Network标签用于显示数据包,对于协议的分析,我们最常用此标签。

主要参数

1. -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=Stackeye -d age=100将自动拼接为name=Stackeye&age=100作为post数据块(post chunk)发送。
如果想指定文件中的内容,可以使用-d @filename的形式。如文件foobar中内容为name=Stackeye,使用-d @foobar即可达到-d name=Stackeye一样的效果(只有第一个=作为特殊字符)。从文件读入时要注意=、@等特殊字符的异常情况。
而-d @-可以指定要发送的内容来自标准输入(stdin)。如执行curl -d @- http://127.0.0.1/welcome.php,在终端输入数据后回车、Ctrl+D后达到相同效果。
对于文件的使用,对于其他类似选项适用。
-d/--data不会对数据进行url编码,而实际场景中我们最常用的还是发送url编码后的数据。

2. --data-urlencode <data>

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

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

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

curl --data-urlencode name=Stackeye%20blog --data-urlencode age=100 \
http://127.0.0.1/welcome.php

wireshark抓包后显示如下:
curl urlencode post数据抓包
在我们的程序中,直接使用-d发送需urlencode编码的数据(一般为除字母数字外的其他字符,如空格,汉字等),程序也会正常处理,那我们为什么还需要使用--data-urlencode呢?原因如下:

  • 能正常处理是apache服务器的原因。实际使用中服务器、程序语言不同,很可能出现无法处理不urlencode编码的数据,尤其是汉字和jsp程序,容易出现乱码
  • 我们在使用浏览器操作时,浏览器会自动进行urlencode操作。为了尽可能逼真,我们使用--data-urlencode尽可能模仿浏览器的操作

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

3. --data-binary <data>

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

4. -G/--get

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

参数使用实例

1. POST

分析post.php可知,此php脚本提交了两个字段:name和age。
打开Developer Tools,填写表单提交后,可看到Network标签下显示出HTTP的POST数据包:
chrome developer tools post分析
注意图中红圈处,可以在“view source”和”view parsed”直接切换,Request Header此时正以source模式显示,此模式下更利于我们的分析。
由此图可看到当前页面(post.php)向welcome.php提交了两个字段name和age,值分别为“Stackeye”和“100”,并可看到服务器的返回信息。

我们可使用curl命令的-d选项模拟此POST过程:

curl -s -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -o welcome.html \
-e http://127.0.0.1/post.php -d name=Stackeye -d age=100 http://127.0.0.1/welcome.php


curl -s -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -o welcome.html \
-e http://127.0.0.1/post.php -d "name=Stackeye;age=100" http://127.0.0.1/welcome.php

因为POST提交表单的过程,一定会有显示表单的页面,因此一般都会有来源页面。因此使用-e指定了post页面为来源页面。
分析可得到curl命令需要使用的几乎所有信息。

注:“几乎所有”的原因是如有些字段是在别的网页传递而来,或者由当前页面的js脚本等生成,此时就需要进一步的分析网页源代码等内容。

2. GET

GET的提交我们直接使用网址后面加参数的形式,使用浏览器打开如下网址:
http://XXX.XXX.XXX.XXX/welcome.php?name=StackeyeGet&age=101
同样使用Developer Tools抓包分析如下:
Developer Tools GET分析
由此图可看到GET向welcome.php提交了两个字段name和age,值分别为“StackeyeGet”和“101”。
我们可以使用curl的-G选项提交:

curl -s -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -o welcome.html \
-e http://127.0.0.1/post.php -G -d name=Stackeye -d age=100 http://127.0.0.1/welcome.php

此时可看到welcome.html中有“This is a GET Request.”表明是由GET提交数据。

实际场景中,表单中指定使用GET提交数据的情况很少。因此-G较少使用。
但我们更常用的还是直接curl带参数的url,此时与手动操作更接近:

curl -s -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -o welcome.html \
"http://127.0.0.1/welcome.php?name=StackeyeGet&age=101"

总结

本篇主要为curl提交表单的相关命令参数。
实际应用中,我们可能还需要使用curl上传文件,相关命令请看下篇。