Как декодировать сжатые данные gzip, возвращаемые в ответе HTTP в python?

Я создал архитектуру клиент/сервер в 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')

Я хочу прочитать данные, которые были переданы между клиентом и вторым сервером.


person vedarthk    schedule 18.03.2012    source источник
comment
Покажите нам код! Вы уверены, что данные не были закодированы/декодированы неправильно (т. е. их следует рассматривать как двоичные данные)?   -  person Cameron    schedule 19.03.2012
comment
Возможно, ваши данные разбиты на несколько фрагментов, и вам нужно проанализировать заголовок, чтобы получить правильную длину. Заголовок, сжатый gzip, содержит информацию о длине.   -  person Jens Munk    schedule 03.04.2016
comment
а если в него попали сами сжатые данные, а вы их ломаете и декодируете только часть вместо всех сжатых данных? Я бы попытался найти \r\n на сервере, прежде чем отправлять его, чтобы проверить, не в нем ли проблема.   -  person Ronen Ness    schedule 04.04.2016


Ответы (2)


Укажите wbits при использовании zlib.decompress(string, wbits, bufsize), см., например, конец раздела «Устранение неполадок».

Исправление проблем

Давайте начнем с команды curl, которая загружает ответ в диапазоне байтов с неизвестной «кодировкой контента» (примечание: мы заранее знаем, что это что-то сжатое, может быть, deflate, может быть, gzip):

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://tools.ietf.org/html/rfc1952#section-2.3.1

Итак, на питоне:

>>> 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
person jmunsch    schedule 06.08.2016

Согласно 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')

Разбиение находит пустую строку, а дополнительный параметр ограничивает количество разбиений - в результате получается массив с двумя элементами, заголовками и телом. Но трудно что-то порекомендовать, не зная больше о вашем коде и фактической разбивке строки.

person Zbyněk Winkler    schedule 07.04.2016
comment
это приводит к тому, что zlib не является текстовой кодировкой - person Eduardo Hernández; 25.09.2020
comment
ну, в то время это был код Python 2, где существовала такая кодировка - для Python 3 docs.python.org/3/library/zlib.html#zlib.decompress необходимо использовать - person Zbyněk Winkler; 28.09.2020