如何从urllib.urlopen()返回的“类文件对象”创建GzipFile实例?

15

我正在使用Python玩弄Stack Overflow API。我试图解码API返回的被压缩的响应。

import urllib, gzip

url = urllib.urlopen('http://api.stackoverflow.com/1.0/badges/name')
gzip.GzipFile(fileobj=url).read()
根据urllib2文档urlopen 返回一个“类文件对象”。
然而,当我在使用它创建的GzipFile对象上运行read()时,会出现以下错误:
AttributeError: addinfourl instance has no attribute 'tell'
据我所知,这个问题来自于urlopen返回的对象。
看起来它也没有seek方法,因为当我尝试使用该方法时会出现错误:
url.read()
url.seek(0)

这个对象究竟是什么,我该如何从它创建一个可用的GzipFile实例?


1
Content-Encoding: gzip 应该由 http 库处理,但不幸的是它没有。对于感兴趣的人来说,这是 Python bug 数据库中的 issue 9500 - Magnus Hoff
@Magnus:谢谢,知道它至少在错误跟踪器中是件好事。 - Paul D. Waite
3个回答

10
urlopen 文档 列出了返回对象支持的方法。我建议将该对象封装在另一个支持 gzip 期望的方法的类中。
另一个选择是调用响应对象的 read 方法,并将结果放入 StringIO 对象中(该对象应支持 gzip 预期的所有方法)。但这可能会更加费时。
例如:
import gzip
import json
import StringIO
import urllib

url = urllib.urlopen('http://api.stackoverflow.com/1.0/badges/name')
url_f = StringIO.StringIO(url.read())
g = gzip.GzipFile(fileobj=url_f)
j = json.load(g)

将其包装在 StringIO 对象中可以解决该错误,但我仍然会得到一个 IOError: Not a gzipped file - Thomas K
1
@ThomasK 对我来说它很好用。你是将 url.read() 传递给 StringIO 构造函数还是只传递了 url?后者会失败。 - aaronasterling
太好了,干杯。Unutbu的答案也很棒,但我会选择这个,因为我猜测StringIO解决方案更具向后兼容性。 - Paul D. Waite
2
有没有一种方法可以在不一次性读取整个urlopen响应的情况下完成这个操作?我想在负载非常大(GB级别)的情况下使用类似的东西,因此我希望能够在数据到达时使用它进行流解析,而不是在整个http请求上阻塞。 - Kevin

8
import urllib2
import json
import gzip
import io

url='http://api.stackoverflow.com/1.0/badges/name'
page=urllib2.urlopen(url)
gzip_filehandle=gzip.GzipFile(fileobj=io.BytesIO(page.read()))
json_data=json.loads(gzip_filehandle.read())
print(json_data)

io.BytesIO是用于Python2.6+的。对于旧版本的Python,您可以使用cStringIO.StringIO


0

这里是@stefanw答案的新更新,对于那些认为使用太多内存太昂贵的人来说。

感谢这篇文章(https://www.enricozini.org/blog/2011/cazzeggio/python-gzip/,它解释了为什么gzip不起作用),解决方案是使用Python3。

import urllib.request
import gzip

response = urllib.request.urlopen('http://api.stackoverflow.com/1.0/badges/name')
with gzip.GzipFile(fileobj=response) as f:
    for line in f:
        print(line)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接