在Python中,我该如何解码GZIP编码?

52

我在我的Python脚本中下载了一个网页。在大多数情况下,这很好用。

然而,这个网页有一个响应头:GZIP编码,而当我尝试打印该网页的源代码时,它出现了所有符号在我的putty中的情况。

如何将其解码为常规文本?

9个回答

105

我使用zlib来解压缩从网络获取的gzip内容。

import zlib
import urllib

f=urllib.request.urlopen(url) 
decompressed_data=zlib.decompress(f.read(), 16+zlib.MAX_WBITS)

12
您能否请评论一下decompress的第二个参数是用来做什么的?为什么它看起来如此...奇怪? - DataGreed
1
@DataGreed,第二个参数是窗口大小,请参考John Machin的下面的回答。 - YOU
你可以使用GzipFile来实现即时解压缩(on-the-fly)。注意:早期版本的GzipFile使用了seek(),但是现在已经修复了这个问题(http://bugs.python.org/issue1675951)。 - jfs

35

使用内置的gzip模块解压缩字节流。

如果遇到任何问题,请显示您使用的精简代码、确切的错误消息和回溯信息以及 print repr(your_byte_stream[:100])的结果。

更多信息

1. 如果要了解gzip/zlib/deflate混淆的解释,请阅读这篇维基百科文章中的“其他用途”部分。

2. 如果您有一个字符串而不是文件,则使用zlib模块可能会更容易。 不幸的是,Python文档是不完整/错误的:

  

zlib.decompress(string[, wbits [, bufsize]])

     

……wbits的绝对值是压缩数据时使用的历史缓冲区(“窗口大小”)的大小的二进制对数。 它的绝对值应为8到15,对于最近版本的zlib库,较大的值会在更大的内存使用代价下产生更好的压缩。 默认值为15。 当wbits为负时,标准gzip头将被抑制; 这是zlib库的未记录特性,用于与unzip的压缩文件格式兼容。

首先,8 <= log2_window_size <= 15,具有上述含义。 然后应该有一个单独的arg:

arg == log2_window_size意味着假设字符串采用zlib格式(RFC 1950;HTTP 1.1 RFC 2616混淆地称为“deflate”)。

arg == -log2_window_size表示假设字符串采用deflate格式(RFC 1951;那些没有仔细阅读HTTP 1.1 RFC的人实际上是这样实现的)

arg == 16 + log_2_window_size表示假设字符串采用gzip格式(RFC 1952)。 因此,您可以使用31。

以上信息记录在zlib C库手册中... 使用Ctrl-F搜索windowBits


19

针对Python 3版本,可以尝试以下方法:

import gzip

fetch = opener.open(request) # basically get a response object
data = gzip.decompress(fetch.read())
data = str(data,'utf-8')

1
请注意,“opener”是可选的,要解码文件,您可以这样做:data = gzip.decompress(open("my.file", "rb").read()); mystring = str(data, "utf-8") - Luc

11

我使用类似于这样的东西:

f = urllib2.urlopen(request)
data = f.read()
try:
    from cStringIO import StringIO
    from gzip import GzipFile
    data2 = GzipFile('', 'r', 0, StringIO(data)).read()
    data = data2
except:
    #print "decompress error %s" % err
    pass
return data

8
如果您使用Requests模块,那么您不需要使用其他任何模块,因为gzipdeflate传输编码会自动为您解码。 示例:
>>> import requests
>>> custom_header = {'Accept-Encoding': 'gzip'}
>>> response = requests.get('https://api.github.com/events', headers=custom_header)
>>> response.headers
{'Content-Encoding': 'gzip',...}
>>> response.text
'[{"id":"9134429130","type":"IssuesEvent","actor":{"id":3287933,...

.text属性是用于读取文本上下文中的内容的响应属性。

.content属性是用于读取二进制上下文中的内容的响应属性。

请参见二进制响应内容部分,位于docs.python-requests.org上。


2

这个版本简单易懂,通过不调用read()方法避免了先读取整个文件的步骤。它提供了一个类似于文件流的对象,其行为与普通文件流完全一致。

import gzip
from urllib.request import urlopen

my_gzip_url = 'http://my_url.gz'
my_gzip_stream = urlopen(my_gzip_url)
my_stream = gzip.open(my_gzip_stream, 'r')

2
与Shatu对于python3的回答类似,但排版略有不同:
import gzip

s = Request("https://someplace.com", None, headers)
r = urlopen(s, None, 180).read()
try: r = gzip.decompress(r)
except OSError: pass
result = json_load(r.decode())

这种方法允许将gzip.decompress()放在try/except语句中,以捕获并传递OSError错误,这种错误发生在可能会混合压缩和未压缩数据的情况下。有些小字符串实际上如果编码后反而会变得更大,因此发送原始数据。


2

这些答案都无法在Python 3中直接使用。以下是我用来获取页面和解码gzip响应的方法:

import requests
import gzip

response = requests.get('your-url-here')
data = str(gzip.decompress(response.content), 'utf-8')
print(data)  # decoded contents of page

-1

你可以使用urllib3轻松解码gzip。

urllib3.response.decode_gzip(response.data)

2
@SamP 我认为因为语句不能直接使用,它并没有解释任何东西。如果你尝试使用它,你肯定会得到一些错误。这根本不是一个有效的答案。 - m3nda
我很喜欢 requests 包,它可以自动处理 gzip - WeizhongTu
2
@SamP 只需使用 urllib3.response.GzipDecoder().decompress(gzip_data) - WeizhongTu
@WeizhongTu,你能让“requests”在头部没有指定时处理“gzip”吗? - Ciprian Tomoiagă

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