如何在Python中解码HTTP响应中返回的gzip压缩数据?

12
我在Python中创建了一个客户端/服务器架构,我从客户端获取HTTP请求,然后通过我的代码请求另一个HTTP服务器提供服务。 当我从第三方服务器获取响应时,我无法解码gzip压缩的数据。首先,我使用\r\n作为分隔符将响应数据拆分为列表中的最后一项,然后尝试使用... 进行解压缩。
zlib.decompress(data[-1]) 

但是它告诉我头部不正确的错误。我该如何解决这个问题呢?

代码

client_reply = ''
                 while 1:
                     chunk = server2.recv(512)
                     if len(chunk) :
                         client.send(chunk)
                         client_reply += chunk
                     else:
                         break
                 client_split = client_reply.split("\r\n")
                 print client_split[-1].decode('zlib')

我想读取客户端和第二个服务器之间传输的数据。


1
展示你的代码!你确定数据没有被错误地编码/解码(即应该将其视为二进制数据)吗? - Cameron
可能是因为您的数据被分成了多个块,您需要解析头部以获取正确的长度。gzipped头部包含长度信息。 - Jens Munk
如果压缩数据本身包含"\r\n",并且您在解压缩时只解码了部分数据而不是全部压缩数据,那该怎么办?我建议在将其发送进行验证之前,在服务器上查找"\r\n"以确认是否存在此问题。 - Ronen Ness
2个回答

7

当使用zlib.decompress(string, wbits, bufsize)时,请指定wbits。示例请参见“故障排除”末尾。

故障排除

我们先来看一个curl命令,该命令下载具有未知“content-encoding”的字节范围响应(注意:我们事先知道它是某种压缩形式,可能是deflategzip):

export URL="https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-18/segments/1461860106452.21/warc/CC-MAIN-20160428161506-00007-ip-10-239-7-51.ec2.internal.warc.gz"
curl -r 266472196-266527075 $URL | gzip -dc | tee hello.txt

以下是响应头的内容:
HTTP/1.1 206 Partial Content
x-amz-id-2: IzdPq3DAPfitkgdXhEwzBSwkxwJRx9ICtfxnnruPCLSMvueRA8j7a05hKr++Na6s
x-amz-request-id: 14B89CED698E0954
Date: Sat, 06 Aug 2016 01:26:03 GMT
Last-Modified: Sat, 07 May 2016 08:39:18 GMT
ETag: "144a93586a13abf27cb9b82b10a87787"
Accept-Ranges: bytes
Content-Range: bytes 266472196-266527075/711047506
Content-Type: application/octet-stream
Content-Length: 54880
Server: AmazonS3

言归正传。

让我们显示前10个字节的十六进制输出: curl -r 266472196-266472208 $URL | xxd

十六进制输出:

0000000: 1f8b 0800 0000 0000 0000 ecbd eb

我们可以从十六进制值中了解一些基本信息。
大致意思是它可能是一个gzip(1f8b),使用deflate(0800)而没有修改时间(0000 0000),或任何额外的标志(00),使用fat32系统(00)。
请参考第2.3 / 2.3.1节:https://www.rfc-editor.org/rfc/rfc1952#section-2.3.1 现在来看Python代码:
>>> import requests
>>> url = 'https://commoncrawl.s3.amazonaws.com/crawl-data/CC-MAIN-2016-18/segments/1461860106452.21/warc/CC-MAIN-20160428161506-00006-ip-10-239-7-51.ec2.internal.warc.gz'
>>> response = requests.get(url, params={"range":"bytes=257173173-257248267"})
>>> unknown_compressed_data = response.content

注意到了什么相似之处吗?
>>> unknown_compressed_data[:10]
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x00'

接下来我们随机尝试一下解压缩,参考文档

>>> import zlib

"zlib.error:在准备解压数据时发生错误-2:流状态不一致"。
>>> zlib.decompress(unknown_compressed_data, -31)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
zlib.error: Error -2 while preparing to decompress data: inconsistent stream state

"

“解压数据时出错-3:头部校验不正确”:

"
>>> zlib.decompress(unknown_compressed_data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
zlib.error: Error -3 while decompressing data: incorrect header check

"zlib.error: 解压数据时出错,错误代码 -3:无效的距离太远":

>>> zlib.decompress(unknown_compressed_data, 30)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
zlib.error: Error -3 while decompressing data: invalid distance too far back

可能的解决方案:

>>> zlib.decompress(unknown_compressed_data, 31)
'WARC/1.0\r\nWARC-Type: response\r\nWARC-Date: 2016-04-28T20:14:16Z\r\nWARC-Record-ID: <urn:uu

1
根据https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html,头和正文之间由仅包含CRLF字符的空行分隔。您可以尝试。
client_split = client_reply.split("\r\n\r\n",1)
print client_split[1].decode('zlib')

分割函数会找到空行,额外的参数限制了分割的次数,结果是一个包含头部和正文两项的数组。但是如果不了解你的代码和实际需要分割的字符串,很难做出具体建议。

这会产生“zlib不是文本编码”的错误。 - Ed_
好的,在当时,这是Python 2代码,其中存在这样的编码 - 对于Python 3,需要使用https://docs.python.org/3/library/zlib.html#zlib.decompress。 - Zbyněk Winkler

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