在Fiddler中发送HTTP Post请求有效,但在Python中无效
我正在使用Fiddler2发送几个POST请求,想检查我的网站是否正常工作。不过,当我用Python自动化这个过程,模拟几个小时的请求时(我真的不想花7个小时不停地按空格键!)。
在Fiddler里这个操作是可以的。我可以创建账户并执行相关的API命令。但是在Python中,这段代码却没有任何反应:
def main():
import socket
from time import sleep
x = raw_input("Points: ")
x = int(x)
x = int(x/150)
for y in range(x):
new = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
new.connect(('example.com', 80))
mydata ="""POST http://www.example.com/api/site/register/ HTTP/1.1
Host: www.example.com
Connection: keep-alive
Content-Length: 191
X-NewRelic-ID: UAIFVlNXGwEFV1hXAwY=
Origin: http://www.example.com
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
X-CSRFToken: CEC9EzYaQOGBdO9HGPVVt3Fg66SVWVXg
DNT: 1
Referer: http://www.example.com/signup
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en;q=0.8
Cookie: sessionid=sessionid; sb-closed=true; arp_scroll_position=600; csrftoken=2u92jo23g929gj2; __utma=912.1.1.2.5.; __utmb=9139i91; __utmc=2019199; __utmz=260270731.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided)
username=user&password=password&moredata=here """
new.send(mydata.encode('hex'))
print "Sent", y, "of", x
sleep(1)
print "Sent all!"
print "restarting"
main()
main()
我知道我可以用While True
来让它一直运行,但我打算以后添加更多功能来测试其他网站。
为什么这个程序在API上没有任何反应,而Fiddler2却可以呢?我知道问题出在我的程序上,因为我可以在Fiddler里发送完全相同的数据包(当然是指向正确的地方),而且它能正常工作。
附言 - 如果有人能解决这个问题,可能是一些很明显的错误,请只使用Python自带的模块。我不能从其他地方安装模块。谢谢!
1 个回答
HTTP请求并没有你想的那么简单。首先,这里有个错误:
"""POST http://www.example.com/api/site/register/ HTTP/1.1
Host: www.example.com
Connection: keep-alive
...
"""
每一行的HTTP请求都必须以CRLF结尾(在Python中是\r\n
),也就是说,它应该是:
"""POST http://www.example.com/api/site/register/ HTTP/1.1\r
Host: www.example.com\r
Connection: keep-alive\r
...
"""
注意:LF = 换行符 = \n
是隐含存在的。而且你在fiddler中看不到CR,因为它是一个空白字符。但它必须存在(简单的复制粘贴是行不通的)。
此外,HTTP还规定在头部之后也必须有CRLF。也就是说,你的整个请求应该是:
mydata = """POST http://www.example.com/api/site/register/ HTTP/1.1\r
Host: www.example.com\r
Connection: keep-alive\r
Content-Length: 191\r
X-NewRelic-ID: UAIFVlNXGwEFV1hXAwY=\r
Origin: http://www.example.com\r
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36\r
Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r
Accept: application/json, text/javascript, */*; q=0.01\r
X-Requested-With: XMLHttpRequest\r
X-CSRFToken: CEC9EzYaQOGBdO9HGPVVt3Fg66SVWVXg\r
DNT: 1\r
Referer: http://www.example.com/signup\r
Accept-Encoding: gzip,deflate,sdch\r
Accept-Language: en-GB,en;q=0.8\r
Cookie: sessionid=sessionid; sb-closed=true; arp_scroll_position=600; csrftoken=2u92jo23g929gj2; __utma=912.1.1.2.5.; __utmb=9139i91; __utmc=2019199; __utmz=260270731.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided)\r
\r
username=user&password=password&moredata=here"""
警告:它必须完全按照我写的那样。每一行前面不能有空格,也就是说,这样:
mydata = """POST http://www.example.com/api/site/register/ HTTP/1.1\r
Host: www.example.com\r
Connection: keep-alive\r
...
"""
就是错误的。
附注:你可以把mydata
移动到循环外面,放到最上面。这虽然是个不重要的优化,但能让你的代码更整洁。
现在你说你使用的网站希望你对HTTP请求进行十六进制编码?我很难相信这一点(HTTP本质上是一个原始字符串)。不要这样做(并且问他们具体这个十六进制编码是什么意思)。可能他们是指URL应该进行十六进制编码(因为这是HTTP中唯一实际使用的十六进制编码)?在你的情况下没有需要编码的内容,所以不用担心。只需删除.encode('hex')
这一行。
另外,Content-Length
头部也搞错了。它应该是内容的实际长度。所以如果比如说请求体是username=user&password=password&moredata=here
,那么它应该是Content-Length: 45
。
接下来,服务器可能不允许你在没有收到响应的情况下发起多个请求。你应该使用new.recv(b)
,其中b
是你想读取的字节数。但你应该读取多少呢?这可能会有点麻烦,这就是Content-Length
头部的用武之地。首先你得读取头部(也就是说,读取到\r\n\r\n
为止,这意味着头部结束),然后你得根据Content-Length
头部来读取主体。正如你所看到的,事情变得有点复杂(见我回答的最后部分)。
你的代码可能还有更多问题。例如,X-CSRFToken
表明该网站使用了CSRF防护机制。在这种情况下,你的请求可能根本无法工作(你需要从服务器获取X-CSRFToken
头部的值)。
最后:不要直接使用套接字。Httplib(http://docs.python.org/2/library/httplib.html)是一个很棒的(内置的)库,用于发起HTTP请求,它会为你处理所有复杂和棘手的HTTP事务。你的代码例如可以这样写:
import httplib
headers = {
"Host": "www.example.com",
"X-NewRelic-ID": "UAIFVlNXGwEFV1hXAwY=",
"Origin": "http://www.example.com",
"Connection": "keep-alive",
"User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Accept": "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": "CEC9EzYaQOGBdO9HGPVVt3Fg66SVWVXg",
"DNT": "1",
"Referer": "http://www.example.com/signup",
"Accept-Encoding": "gzip,deflate,sdch",
"Accept-Language": "en-GB,en;q=0.8",
"Cookie": "sessionid=sessionid; sb-closed=true; arp_scroll_position=600; csrftoken=2u92jo23g929gj2; __utma=912.1.1.2.5.; __utmb=9139i91; __utmc=2019199; __utmz=260270731.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided)"
}
body = "username=user&password=password&moredata=here"
conn = httplib.HTTPConnection("example.com")
conn.request("POST", "http://www.example.com/api/site/register/", body, headers)
res = conn.getresponse()
注意,你不需要指定Content-Length
头部。