切换语言为:繁体

如何将 urllib2 升级至 pycurl

  • 爱糖宝
  • 2024-11-13
  • 2026
  • 0
  • 0

在 Python 中,urllib2 是一个用于处理 HTTP 请求的模块,但它在 Python 3 中被拆分成 urllib.requesturllib.error。相比之下,pycurl 提供了更高效的 HTTP 传输方式,因为它直接基于 libcurl,可以支持更多协议和更好的性能。因此,有时需要将 urllib2 的代码迁移到 pycurl

1、问题背景

我有一段代码(如下所示),它使用 urllib2 库。我想将其转换为 pycurl 库,以便受益于 pycurl 的代理支持。pycurl 的转换代码在原始代码之后。我想知道如何将 urllib.urlopen(req).read() 更改为 pycurl 中类似的方法,也许可以使用 StringIO 之类的方法?

urllib2 代码:

URL = 'URL'
UN = 'UN'
PWD = 'PWD'
HEADERS = { 'Accept': 'application/json',
            'Connection': 'Keep-Alive',
            'Accept-Encoding' : 'gzip',
            'Authorization' : 'Basic %s' % base64.encodestring('%s:%s' % (UN, PWD))  }
req = urllib2.Request(URL, headers=HEADERS)
response = urllib2.urlopen(req, timeout=(KEEP_ALIVE))
# header -  print response.info()
decompressor = zlib.decompressobj(16+zlib.MAX_WBITS)
remainder = ''
while True:
    tmp = decompressor.decompress(response.read(CHUNKSIZE))

pycurl 转换代码(带有代理支持):

URL = 'URL'
UN = 'UN'
PWD = 'PWD'
HEADERS = [ 'Accept : application/json',
            'Connection : Keep-Alive',
            'Accept-Encoding : gzip',
            'Authorization : Basic %s' % base64.encodestring('%s:%s' % (UN, PWD))  ]
req = pycurl.Curl()
req.setopt(pycurl.CONNECTTIMEOUT,KEEP_ALIVE)
req.setopt(pycurl.HTTPHEADER, HEADERS)
req.setopt(pycurl.TIMEOUT, 1+KEEP_ALIVE)
req.setopt(pycurl.PROXY, 'http://my-proxy')
req.setopt(pycurl.PROXYPORT, 8080)
req.setopt(pycurl.PROXYUSERPWD, "proxy_access_user : proxy_access_password")
req.setopt(pycurl.URL , URL)
response = req.perform()
decompressor = zlib.decompressobj(16+zlib.MAX_WBITS)
remainder = ''
while True:
    tmp = decompressor.decompress(urllib2.urlopen(req).read(CHUNKSIZE))

2、解决方案

与 urllib2(它返回一个可用于获取数据的对象)不同,curl 需要您传递一个它可以用来存储数据的对象。 最简单的方法(在大多数示例中使用)是将文件对象作为 WRITEDATA 选项传递。您可能认为可以像这样直接传递一个 StringIO:

s = StringIO.StringIO()
req.setopt(pycurl.WRITEDATA, s)
req.perform()
data = s.getvalue()

不幸的是,这不起作用,因为文件对象必须是真实的文件(或至少是具有 C 级文件描述符的东西),而 StringIO 不符合要求。

当然,您可以使用 NamedTemporaryFile,但如果您希望将文件保存在内存中——或者更好地说是不用保存在内存或磁盘中,而是直接处理它——那这帮不了您。

解决方法是改用 WRITEFUNCTION 选项:

s = StringIO.StringIO()
req.setopt(pycurl.WRITEFUNCTION, s.write)
req.perform()
data = s.getvalue()

如您所见,您可以根据需要使用 StringIO,实际上,这就是 pycurl 对象文档所做的,但这并不会比其他任何累积字符串的方法简单太多(例如,将它们放入列表然后执行 ''.join,甚至只是将它们连接到字符串上)。

请注意,我链接的是 C 级 libcurl 文档,而不是 pycurl 文档,因为 pycurl 的文档基本上只是说“FOO 与 CURLOPT_FOO 做同样的事情”(即使存在差异,例如您的 WRITEFUNCTION 不获取大小,nmemb 和 userdata 参数)。

如果您想实时传输数据,该怎么办?只需使用一个累积并即时处理数据的 WRITEFUNCTION。您无需自己编写循环,但 curl 将在内部循环并驱动进程。例如:

z = zlib.decompressobj()
s = []
def handle(chunk):
    s.append(z.decompress(chunk))
    return len(chunk)
req.setopt(pycurl.WRITEFUNCTION, handle)
req.perform()
s.append(z.flush())
data = ''.join(s)

curl 将在检索到每块数据时调用您的函数一次,因此整个循环都发生在 req.perform() 调用中。(它也可能会在最后再次调用它,但您得确保您的回调函数能够处理这种情况。我认为 z.decompress 可以,但您可能需要验证一下。)

有一些方法可以限制每次写入的大小,在中间中止下载,将标头作为写入的一部分而不是单独获取,等等,但通常您不需要接触这些方法。

划重点

  • pycurl 使用 Curl 对象来管理 HTTP 请求设置,setopt 方法来配置不同的参数。

  • pycurl 提供了更灵活的选项,比如自定义请求头、超时设置、自动处理重定向等功能。

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.