2018 Обновление:
По состоянию на февраль 2018 года использование таких сжатий gzip
стало довольно популярным (около 73% всех веб-сайтов используют его, включая такие крупные сайты, как Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow и Stack Exchange Network).
Если вы выполните простое декодирование, как в исходном ответе с gzipped ответом, вы получите сообщение об ошибке, похожее на это:
UnicodeDecodeError: кодек «utf8» не может декодировать байт 0x8b в позиции 1: неожиданный байт кода
Чтобы декодировать ответ gzpipped, вам необходимо добавить следующие модули (в Python 3):
import gzip
import io
Примечание: в Python 2 вы бы использовали StringIO
вместоio
Затем вы можете разобрать содержимое следующим образом:
response = urlopen("https://example.com/gzipped-ressource")
buffer = io.BytesIO(response.read()) # Use StringIO.StringIO(response.read()) in Python 2
gzipped_file = gzip.GzipFile(fileobj=buffer)
decoded = gzipped_file.read()
content = decoded.decode("utf-8") # Replace utf-8 with the source encoding of your requested resource
Этот код читает ответ и помещает байты в буфер. Затем gzip
модуль читает буфер, используя GZipFile
функцию. После этого файл gzipped можно снова прочитать в байты и в конце декодировать в нормально читаемый текст.
Оригинальный ответ от 2010 года:
Можем ли мы получить фактическое значение, используемое для link
?
Кроме того, мы обычно сталкиваемся с этой проблемой, когда пытаемся .encode()
использовать уже закодированную строку байтов. Таким образом, вы можете попытаться декодировать его сначала, как в
html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")
Например:
html = '\xa0'
encoded_str = html.encode("utf8")
Сбой с
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 0: ordinal not in range(128)
Пока:
html = '\xa0'
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")
Успешно без ошибок. Обратите внимание, что «windows-1252» - это то, что я использовал в качестве примера . Я получил это от Chardet, и у него было 0,5 уверенности, что это правильно! (ну, как и в случае со строкой длиной в 1 символ, что вы ожидаете) Вы должны изменить это на кодирование строки байтов, возвращаемой .urlopen().read()
на то, что относится к содержимому, которое вы получили.
Другая проблема, которую я вижу, заключается в том, что .encode()
метод string возвращает измененную строку и не изменяет источник на месте. Так что это бесполезно иметь, self.response.out.write(html)
поскольку html не является закодированной строкой из html.encode (если это то, к чему вы изначально стремились).
Как предложил Игнасио, проверьте исходную веб-страницу на предмет фактической кодировки возвращаемой строки из read()
. Это либо в одном из мета-тегов, либо в заголовке ContentType в ответе. Используйте это тогда как параметр для .decode()
.
Однако обратите внимание, что не следует предполагать, что другие разработчики несут достаточную ответственность, чтобы убедиться, что объявления заголовка и / или набора метасимволов соответствуют фактическому содержанию. (Что такое PITA, да, я должен знать, я был одним из них раньше).