【问题标题】:migrate from urllib2 to pycurl从 urllib2 迁移到 pycurl
【发布时间】:2013-10-07 18:15:55
【问题描述】:

我有一个使用 urllib2 的 sn-p 代码,如下所示。我正在尝试将其转换为 pycurl 以受益于 pycurl 代理支持。 pycurl 的转换代码显示在原始代码之后。我想知道如何将 urllib.urlopen(req).read() 更改为 pycurl 中的类似内容。也许使用类似 strinIO 的东西?

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))

提前致谢。

【问题讨论】:

  • @abarnert 谢谢.. 我会编辑这个问题。

标签: python urllib2 pycurl


【解决方案1】:

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 — 事实上,这正是来自 pycurlcurl 对象文档所做的 — 但它不是真的与任何其他累积字符串的方式相比,简化了太多事情(比如将它们放在一个列表中并''.join-ing 它们,甚至只是将它们连接到一个字符串上)。

请注意,我链接到 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() 调用中。 (它也可能在最后以 0 字节再次调用它,因此请确保您的回调函数可以处理它。我认为 z.decompress 可以,但您可能想验证这一点。)

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

【讨论】:

    猜你喜欢
    • 2016-06-17
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    • 1970-01-01
    • 2012-12-04
    • 2011-04-26
    • 2015-07-23
    • 2020-05-31
    相关资源
    最近更新 更多