HTTPS POST请求Python

9
我希望向一个HTTPS网站发起POST请求,该网站应该用.csv文件进行响应。 我有以下Python代码:
url = 'https://www.site.com/servlet/datadownload'
values = {
  'val1' : '123',
  'val2' : 'abc',
  'val3' : '1b3',
}

data = urllib.urlencode(values)
req = urllib2.Request(url,data)
response = urllib2.urlopen(req)
myfile = open('file.csv', 'wb')
shutil.copyfileobj(response.fp, myfile)
myfile.close()

但是我收到了错误提示:
BadStatusLine: ''    (in httplib.py)

我已经尝试使用Chrome扩展程序:高级REST客户端(截图)进行POST请求,它可以正常工作。

可能的问题是什么,如何解决?(是因为HTTPS吗?)


编辑,重构代码:

try:
    #conn = httplib.HTTPSConnection(host="www.site.com", port=443)

=> 会出现BadStatusLine: ''错误。

    conn = httplib.HTTPConnection("www.site.com");
    params  = urllib.urlencode({'val1':'123','val2':'abc','val3':'1b3'})
    conn.request("POST", "/nps/servlet/exportdatadownload", params)
    content = conn.getresponse()
    print content.reason, content.status
    print content.read()
    conn.close()
except:
    import sys
    print sys.exc_info()[:2]

输出:

Found 302

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>302 Found</TITLE>
</HEAD><BODY>
<H1>Found</H1>
The document has moved <A HREF="https://www.site.com/nps/servlet/exportdatadownload">here</A>.<P>
<HR>
<ADDRESS>Oracle-Application-Server-10g/10.1.3.5.0 Oracle-HTTP-Server Server at mp-www1.mrco.be Port 7778</ADDRESS>
</BODY></HTML>

我做错了什么?


1
你使用的是哪个版本的Python?我建议查看这个答案,以确定httplib是否能够正常处理https。我现在无法尝试你的代码,但另一个建议是使用一个更友好的请求库,名为... requests - Geekfish
如果您使用https_handler = urllib2.HTTPSHandler(1) opener = urllib2.build_opener(https_handler) response = opener.open(req)代替response = urllib2.urlopen(req),会得到什么结果?您仍然应该会收到错误信息,但这将打开https响应的调试功能,这意味着您的响应将被打印出来,您可以利用它来帮助跟踪问题所在。如果由于某种奇怪的原因而使用了另一个处理程序,请尝试使用urllib2.HTTPHandler(1)或其他相关处理程序进行相同的操作。 - Silas Ray
我注意到你同时在使用urllib和urllib2,这是有意为之吗? - Josh
你应该发布这个网站。 - user1786283
4个回答

14

你有必须使用 urllib 的原因吗?Requests 更简单,几乎在所有方面都更好,并且抽象出了一些使 urllib 难以处理的杂项。

例如,我会将你的示例重新编写为:

import requests
resp = requests.post(url, data=values, allow_redirects=True)

在这一点上,服务器的响应可在resp.text中获取,您可以根据需要对其进行处理。如果请求无法正确执行POST操作(例如,您需要自定义SSL证书),它应该会提供一个漂亮的错误消息,告诉您原因。
即使在生产环境中无法执行此操作,也可以在本地shell中执行此操作,以查看从requests获取的错误消息,并使用其来调试urllib

相同的错误:BadStatusLine: ConnectionError: HTTPSConnectionPool(host='www.site.com', port=443): Max retries exceeded with url: /nps/servlet/exportdatadownload/ (Caused by <class 'httplib.BadStatusLine'>: '') 当我访问 https://www.site.com/nps/servlet/exportdatadownload?val1=123&val2=abc&val3=1b3 时,Excel 文件会自动下载,但是用 Python 脚本仍然失败... - francisMi
"BadStatusLine" 表示服务器返回了 Python 不理解的 HTTP 状态(它理解所有“正常”的状态)。您能否从命令行执行 curl -I https://site.com(其中实际 URL 是什么就填什么),并粘贴结果?如果您没有 curl,也可以使用 hurl.it(在这种情况下,我只对响应的第一段感兴趣)。 - Dan

3
BadStatusLine: '' (in httplib.py)提示可能存在其他问题。当服务器根本没有回复并关闭连接时,可能会出现这种情况。
由于您提到正在使用SSL连接,因此如果您想进行调试,可以尝试使用curl -v URL命令。如果发现curl -2 URL(强制使用SSLv2)似乎有效,而curl -3 URL(SSLv3)无法正常工作,则可能需要查看Python bugtracker上的问题#13636#11220。根据您的Python版本和可能配置不正确的Web服务器,这可能会导致问题:SSL默认值在v2.7.3中已更改。

1
   conn = httplib.HTTPSConnection(host='www.site.com', port=443, cert_file=_certfile)
   params  = urllib.urlencode({'cmd': 'token', 'device_id_st': 'AAAA-BBBB-CCCC',
                                'token_id_st':'DDDD-EEEE_FFFF', 'product_id':'Unit Test',
                                'product_ver':"1.6.3"})
    conn.request("POST", "servlet/datadownload", params)
    content = conn.getresponse().read()
    #print response.status, response.reason
    conn.close()

我尝试了你的代码,但将第一行改为httplib.HTTPSConnection('www.site.com')。当我打印content.status时,我得到了Found 302。并且打印内容本身,我得到了带有The document has moved <A HREF="https://www.site.com/servlet/exportdatadownload">here</A>.<P>的HTML代码。但是我该如何获取找到的文件? - francisMi
我已经编辑了我的问题,并附上了更多信息和您的代码。 - francisMi
尝试访问网址 https://google.com,感觉您可能遇到了某种服务器/目标地址问题。 - bioffe
httplib.HTTPSConnection(host="www.google.com", port=443) 返回 Not Found 404 输出,而 httplib.HTTPConnection("www.google.com") 则返回 Service Unavailable 503 - francisMi
很好。谷歌网站上没有/servlet/datadownload URL,因此出现了错误。现在我有信心认为问题出在您的服务器上。尝试阅读一些简单的内容,比如静态HTML页面(可以通过浏览器访问)。 - bioffe
通常,当我在浏览器中尝试请求时,会自动下载一个 .csv 文件。所以这是否意味着服务器重定向了响应,我需要使用 Python 代码来“跟随”它? - francisMi

0
服务器可能会不喜欢缺少头信息,特别是用户代理和内容类型。Chrome 图像显示了这些内容的使用情况。可以尝试添加以下头信息:
import httplib, urllib

host = 'www.site.com'
url = '/servlet/datadownload'

values = {
  'val1' : '123',
  'val2' : 'abc',
  'val3' : '1b3',
}

headers = {
    'User-Agent': 'python',
    'Content-Type': 'application/x-www-form-urlencoded',
}

values = urllib.urlencode(values)

conn = httplib.HTTPSConnection(host)
conn.request("POST", url, values, headers)
response = conn.getresponse()

data = response.read()

print 'Response: ', response.status, response.reason
print 'Data:'
print data

这是未经测试的代码,您可能希望尝试添加其他标题值以匹配您的屏幕截图。希望能够帮到您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接