在Fiddler中发送HTTP Post请求有效,但在Python中无效

1 投票
1 回答
2201 浏览
提问于 2025-04-17 22:44

我正在使用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 个回答

2

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头部。

撰写回答