Странности чтения Python3 urlopen (gzip)

Я получаю URL от Schema.org. Это content-type="text/html"

Иногда read() работает как положено b'‹ !DOCTYPE html> ....'

Иногда read() возвращает что-то еще b'\x1f\x8b\x08\x00\x00\x00\x00...'

try:
    with urlopen("http://schema.org/docs/releases.html") as f:
        txt = f.read()
except URLError:
    return

Я пытался решить эту проблему с помощью txt = f.read().decode("utf-8").encode(), но это приводит к ошибке... иногда: UnicodeDecodeError: кодек 'utf-8' не может декодировать байт 0x8b в позиции 1: недопустимый начальный байт

Очевидный обходной путь — проверить, является ли первый байт шестнадцатеричным, и соответствующим образом обработать его.

У меня вопрос: это баг или что-то другое?

введите описание изображения здесь Изменить Связанные вопрос. По-видимому, иногда я получаю gzip-поток.

Наконец я решил эту проблему, добавив следующий код как предложено здесь

if 31 == txt[0]:
    txt = decompress(txt, 16+MAX_WBITS)

Остается вопрос; почему это иногда возвращает text/html, а иногда заархивировано?


person GUI Junkie    schedule 25.08.2015    source источник
comment
Я могу только воспроизвести случай, когда вы получаете b'‹!DOCTYPE ... Мне кажется, что другой ответ, который вы получаете, как-то связан с тем или иным сбоем вашего интернет-соединения.   -  person Jaap Versteegh    schedule 25.08.2015
comment
@SlashV Я получаю это, может быть, раз в 5 раз   -  person GUI Junkie    schedule 25.08.2015
comment
Я запускал код 200 раз...   -  person Jaap Versteegh    schedule 25.08.2015
comment
@SlashV Я использую PyCharm, не знаю   -  person GUI Junkie    schedule 25.08.2015
comment
@SlashV Добавлен скриншот   -  person GUI Junkie    schedule 25.08.2015
comment
Теперь я вижу ваше редактирование. Когда вы получаете заархивированный поток, вам, очевидно, придется сначала разархивировать его. Возможно, вы сможете избежать получения заархивированного ответа, добавив заголовок Accept.   -  person Jaap Versteegh    schedule 25.08.2015
comment
@SlashV Круто, я так и сделаю. Я видел ряд вопросов, связанных с заархивированными потоками на StackOverflow. Должен ли я удалить этот вопрос?   -  person GUI Junkie    schedule 25.08.2015
comment
@SlashV Разве это не должно быть Accept-Encoding?   -  person dhke    schedule 25.08.2015
comment
@dhke ага. В частности, я считаю, что Accept-Encoding: identity должно помочь   -  person Jaap Versteegh    schedule 25.08.2015
comment
@SlashV Да. urlopen() вообще не указывает Accept-Encoding, который сервер ранее МОЖЕТ интерпретировать как Accept-Encoding: *. Это изменилось с помощью RFC7231. Судя по истории вопроса, я бы действительно посчитал это случаем вики-ответа.   -  person dhke    schedule 25.08.2015
comment
Я не могу получить сжатые данные с этого URL-адреса, даже если я укажу Accept-Encoding: gzip;q=1 или Accept-Encoding: gzip, deflate, поэтому мне кажется, что у этого сервера есть некоторые собственные правила.   -  person Jaap Versteegh    schedule 25.08.2015


Ответы (2)


В этой категории есть и другие вопросы, но я не могу найти ответ, который касается фактической причины проблемы.

Python urllib2.urlopen() не может прозрачно обрабатывать сжатие. Он также по умолчанию не устанавливает заголовок запроса Accept-Encoding. Кроме того, интерпретация этой ситуации в соответствии со стандартом HTTP в прошлом изменилась.

Согласно RFC2616:

Если в запросе нет поля Accept-Encoding, сервер МОЖЕТ предположить, что клиент примет любое кодирование контента. В этом случае, если «идентификация» является одной из доступных кодировок контента, то сервер ДОЛЖЕН использовать кодировку контента «идентификация», если у него нет дополнительной информации о том, что другое кодирование контента имеет смысл. клиенту.

К сожалению (что касается варианта использования), RFC7231 изменяет это на

Если в запросе нет поля Accept-Encoding, агент пользователя считает приемлемым любое кодирование содержимого.

Это означает, что при выполнении запроса с использованием urlopen() вы можете получить ответ в любой кодировке, которую решит использовать сервер, и ответ будет соответствующим.

schema.org, по-видимому, размещен в Google, т. е. скорее всего, он находится за распределенной сетью внешнего балансировщика нагрузки. Таким образом, разные ответы, которые вы получаете, могут быть возвращены балансировщиками нагрузки с немного отличающимися конфигурациями.

В прошлом инженеры Google использовали выступает за использование HTTP-сжатия, так что это вполне может быть сознательным решением.

Итак, урок: при использовании urlopen() нам нужно установить Accept-Encoding.

person dhke    schedule 25.08.2015
comment
Я подозревал что-то подобное. Балансировщик нагрузки кажется разумным объяснением. Ваше здоровье. - person GUI Junkie; 25.08.2015

Вы действительно получаете сжатый ответ. Вы должны быть в состоянии избежать этого:

from urllib import request
try:
    req = request.Request("http://schema.org/docs/releases.html")
    req.add_header('Accept-Encoding', 'identity;q=1')
    with request.urlopen(req) as f:
        txt = f.read()
except request.URLError:
    return
person Jaap Versteegh    schedule 25.08.2015
comment
Обходной путь, который я выбрал, - распаковать его... меньше кода - person GUI Junkie; 25.08.2015
comment
@GUIJunkie нет, обе строки состоят из двух строк, и вам придется сделать дополнительный импорт для decompress ;) - person Jaap Versteegh; 25.08.2015
comment
:-) Мне также пришлось сделать дополнительный импорт для запроса. Пожимает плечами. - person GUI Junkie; 25.08.2015