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()
возвращает измененную строку и не изменяет источник на месте. Поэтому бесполезно иметь self.response.out.write(html)
, поскольку html не является закодированной строкой из html.encode (если это то, к чему вы изначально стремились).
Как предложил Игнасио, проверьте исходную веб-страницу на предмет фактической кодировки возвращаемой строки из read()
. Это либо в одном из мета-тегов, либо в заголовке ContentType в ответе. Затем используйте это как параметр для .decode()
.
Обратите внимание, однако, что не следует предполагать, что другие разработчики несут достаточную ответственность, чтобы убедиться, что объявления заголовка и / или набора метасимволов соответствуют фактическому содержанию. (Что такое PITA, да, я должен знать, я был одним из них раньше).