如何在Python urllib2中实现GET与POST的流切换?
我想写代码,把一个文件从一个网站传到另一个网站。这个文件可能很大,我希望能直接传,而不需要先在本地创建一个临时文件。
我看到有个方法是用mmap在Python中上传大文件,叫做“用流式传输HTTP Post一个大文件”,但我真正需要的是一种方法,可以把GET请求的响应和创建POST请求连接起来。
有没有人之前做过这个?
2 个回答
urllib2 可能对这个任务来说太简单了。你可以考虑看看 pycurl。我知道它支持流式传输。
你不能,或者说不应该这么做。
urllib2
的请求对象不能实时接收数据,根本没这个功能。而且,响应对象就像文件一样,理论上你可以用 read(8192)
从中读取数据,而不是用 read()
,但对于大多数协议,包括 HTTP,它通常会把整个响应都读入内存,然后再从缓存中提供你的 read(8192)
调用,这样做就没什么意义了。所以,你需要拦截请求,手动处理套接字,这时候 urllib2
反而会成为你的障碍,而不是帮助。
urllib2
有些事情做得简单,有些事情却比应该的要难得多,还有些事情几乎不可能;当它不再让事情变简单时,就别再用它了。
一个解决方案是使用更高级的第三方库。例如,requests
可以让你轻松处理响应流,但在某些情况下只能有限地处理请求流,而 requests-toolbelt
则可以帮助你完成剩下的工作(它提供了多种流式上传的方法)。
另一个解决方案是使用更底层的库。在这里,你甚至不需要离开标准库。httplib
让你必须逐步思考发送和接收数据,但这正是你想要的。在 GET 请求中,你可以直接调用 connect
和 request
,然后在响应对象上反复调用 read(8192)
。在 POST 请求中,你调用 connect
、putrequest
、putheader
、endheaders
,然后反复 send
每个缓冲区,最后在完成后调用 getresponse
。
实际上,在 Python 3.2 及以上版本的 http.client
(相当于 2.x 的 httplib
)中,HTTPClient.request
不一定要是字符串,它可以是任何可迭代对象或任何具有 read
和 fileno
方法的文件样对象……这包括响应对象。所以,事情其实很简单:
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
更高级还是更低级,它在复杂性上有自己独特的分类。 :)