如何在Python urllib2中实现GET与POST的流切换?

3 投票
2 回答
2450 浏览
提问于 2025-04-18 14:22

我想写代码,把一个文件从一个网站传到另一个网站。这个文件可能很大,我希望能直接传,而不需要先在本地创建一个临时文件。

我看到有个方法是用mmap在Python中上传大文件,叫做“用流式传输HTTP Post一个大文件”,但我真正需要的是一种方法,可以把GET请求的响应和创建POST请求连接起来。

有没有人之前做过这个?

2 个回答

0

urllib2 可能对这个任务来说太简单了。你可以考虑看看 pycurl。我知道它支持流式传输。

3

你不能,或者说不应该这么做。

urllib2 的请求对象不能实时接收数据,根本没这个功能。而且,响应对象就像文件一样,理论上你可以用 read(8192) 从中读取数据,而不是用 read(),但对于大多数协议,包括 HTTP,它通常会把整个响应都读入内存,然后再从缓存中提供你的 read(8192) 调用,这样做就没什么意义了。所以,你需要拦截请求,手动处理套接字,这时候 urllib2 反而会成为你的障碍,而不是帮助。

urllib2 有些事情做得简单,有些事情却比应该的要难得多,还有些事情几乎不可能;当它不再让事情变简单时,就别再用它了。


一个解决方案是使用更高级的第三方库。例如,requests 可以让你轻松处理响应流,但在某些情况下只能有限地处理请求流,而 requests-toolbelt 则可以帮助你完成剩下的工作(它提供了多种流式上传的方法)。


另一个解决方案是使用更底层的库。在这里,你甚至不需要离开标准库。httplib 让你必须逐步思考发送和接收数据,但这正是你想要的。在 GET 请求中,你可以直接调用 connectrequest,然后在响应对象上反复调用 read(8192)。在 POST 请求中,你调用 connectputrequestputheaderendheaders,然后反复 send 每个缓冲区,最后在完成后调用 getresponse

实际上,在 Python 3.2 及以上版本的 http.client(相当于 2.x 的 httplib)中,HTTPClient.request 不一定要是字符串,它可以是任何可迭代对象或任何具有 readfileno 方法的文件样对象……这包括响应对象。所以,事情其实很简单:

import http.client

getconn = httplib.HTTPConnection('www.example.com')
getconn.request('GET', 'http://www.example.com/spam')
getresp = getconn.getresponse()

getconn = httplib.HTTPConnection('www.example.com')
getconn.request('POST', 'http://www.example.com/eggs', body=getresp)
getresp = getconn.getresponse()

……当然,你可能还想设置合适的请求头(实际上你可以使用 urllib.request,这是 3.x 版本的 urllib2,来构建一个 Request 对象而不发送它……),并用 urlparse 从 URL 中提取主机和端口,而不是硬编码它们,还想检查或至少处理 POST 请求的响应等等。但这展示了困难的部分,其实并不难。

不幸的是,我认为这在 2.x 中是行不通的。


最后,如果你熟悉 libcurl,那么至少有三个它的封装(包括一个随源代码发布的)。我不太确定该把 libcurl 归类为比 urllib2 更高级还是更低级,它在复杂性上有自己独特的分类。 :)

撰写回答