使用Python Requests库上传大型XML文件

时间:2012-11-11 13:42:03

标签: python xml python-requests

我正在尝试用Python&替换curl请求库。使用curl,我可以使用curl -T选项将单个XML文件上载到REST服务器。我无法对请求库做同样的事情。

基本情景有效:

payload = '<person test="10"><first>Carl</first><last>Sagan</last></person>'
headers = {'content-type': 'application/xml'}
r = requests.put(url, data=payload, headers=headers, auth=HTTPDigestAuth("*", "*"))

当我通过打开XML文件将有效负载更改为更大的字符串时,.put方法挂起(我使用编解码器库来获取正确的unicode字符串)。例如,使用66KB文件:

xmlfile = codecs.open('trb-1996-219.xml', 'r', 'utf-8')
headers = {'content-type': 'application/xml'}
content = xmlfile.read()
r = requests.put(url, data=content, headers=headers, auth=HTTPDigestAuth("*", "*"))

我一直在研究使用multipart选项(文件),但服务器似乎不喜欢这样。

所以我想知道是否有办法模拟Python请求库中的curl -T行为。

更新1: 该程序挂起textmate,但在命令行上抛出UnicodeEncodeError错误。似乎一定是问题所在。所以问题是:有没有办法将unicode字符串发送到带有请求库的服务器?

更新2: 感谢Martijn Pieters的评论,UnicodeEncodeError消失了,但是出现了一个新问题。 使用文字(ASCII)XML字符串,日志记录显示以下行:

2012-11-11 15:55:05,154 INFO Starting new HTTP connection (1): my.ip.address
2012-11-11 15:55:05,294 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 401 211
2012-11-11 15:55:05,430 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 201 0

似乎服务器总是跳出第一次身份验证尝试(?),但随后接受第二次身份验证尝试。

将文件对象(open('trb-1996-219.xml','rb'))传递给数据,日志文件显示:

2012-11-11 15:50:54,309 INFO Starting new HTTP connection (1): my.ip.address
2012-11-11 15:50:55,105 DEBUG "PUT /v1/documents?uri=/example/test.xml HTTP/1.1" 401 211
2012-11-11 15:51:25,603 WARNING Retrying (0 attempts remain) after connection broken by 'BadStatusLine("''",)': /v1/documents?uri=/example/test.xml

因此,首次尝试被阻止,但没有第二次尝试。

根据Martijn Pieters(下文),第二个问题可以用错误的服务器(空行)来解释。 我将研究这个,但如果有人有一个解决方法(除了使用卷曲)我不介意听到它。

我仍然感到惊讶的是,请求库对于小字符串和文件对象的行为方式如此不同。文件对象在到达服务器之前是否已序列化?

3 个答案:

答案 0 :(得分:9)

要PUT大文件,请不要将它们读入内存。只需将文件作为data关键字传递:

xmlfile = open('trb-1996-219.xml', 'rb')
headers = {'content-type': 'application/xml'}
r = requests.put(url, data=xmlfile, headers=headers, auth=HTTPDigestAuth("*", "*"))

此外,您将文件作为unicode打开(从UTF-8解码)。当您将其发送到远程服务器时,您需要原始字节,而不是unicode值,您应该将文件作为二进制文件打开。

答案 1 :(得分:1)

摘要式身份验证始终要求您至少向服务器发出两个请求。第一个请求不包含任何身份验证数据。第一个请求将失败,401“需要授权”响应代码和摘要质询(称为nounce)将用于散列您的密码等(具体细节在这里无关紧要)。这用于向服务器发出第二个请求,其中包含带有挑战的哈希凭证。

问题在于这两步认证:您的大文件已经发送了第一个未经授权的请求(徒劳发送),但在第二个请求中,文件对象已经处于EOF位置。由于文件大小也是在第二个请求的Content-length标头中发送的,这会导致服务器等待永远不会发送的文件。

您可以使用请求会话来解决它,并首先发出一个简单的身份验证请求(例如GET请求)。然后使用与第一个请求相同的摘要质询,发出包含实际有效负载的第二个PUT请求。

sess = requests.Session()
sess.auth = HTTPDigestAuth("*", "*")
sess.get(url)
headers = {'content-type': 'application/xml'}
with codecs.open('trb-1996-219.xml', 'r', 'utf-8') as xmlfile:
    sess.put(url, data=xmlfile, headers=headers)

答案 2 :(得分:0)

我使用python中的请求使用命令上传XML文件。 首先打开文件使用open()     file = open("PIR.xsd")     fragment = file.read()     file.close() 将XML文件的数据复制到请求的有效负载中并发布     payload = {'key':'PFAkrzjmuZR957','xmlFragment':fragment}     r = requests.post(URL,data=payload) 检查html验证码     print (r.text)