Преобразуйте Unicode в ASCII без ошибок в Python

Мой код просто очищает веб-страницу, затем преобразовывает ее в Unicode.

html = urllib.urlopen(link).read()
html.encode("utf8","ignore")
self.response.out.write(html)

Но я получаю a UnicodeDecodeError:


Traceback (most recent call last):
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/webapp/__init__.py", line 507, in __call__
    handler.get(*groups)
  File "/Users/greg/clounce/main.py", line 55, in get
    html.encode("utf8","ignore")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 2818: ordinal not in range(128)

Я предполагаю, что означает, что HTML содержит некоторую неправильно сформированную попытку Unicode где-нибудь. Я могу просто отбросить любые байты кода, вызывают проблему вместо того, чтобы получить ошибку?

170
задан Martin Thoma 30 January 2018 в 14:35
поделиться

5 ответов

Обновление 2018 г .:

По состоянию на февраль 2018 г. использование сжатия типа gzip стало довольно популярным ( около 73% всех веб-сайтов используют его, включая такие крупные сайты, как Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow и Stack Exchange Network).
Если вы выполните простое декодирование, как в исходном ответе, с помощью gzip-ответа, вы получите ошибку, подобную или похожую на эту:

UnicodeDecodeError: кодек 'utf8' не может декодировать байт 0x8b в позиции 1: неожиданный байт кода

Для декодирования ответа, отправленного с помощью gzpip, вам необходимо добавить следующие модули (в 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 . После этого сжатый файл может быть снова прочитан в байты и в конце концов декодирован в нормально читаемый текст.

Исходный ответ от 2010 г .:

Можем ли мы получить фактическое значение, используемое для ссылки ?

Кроме того, мы обычно сталкиваемся с этой проблемой здесь, когда пытаемся .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, да, я должен знать, я был одним из них раньше).

102
ответ дан 23 November 2019 в 20:45
поделиться

Если у вас есть строка строка , вы можете использовать .encode ([кодировка] , [errors = 'strict']) метод для строк для преобразования типов кодировки.

line = 'my big string'

line.encode ('ascii', 'ignore')

Подробнее информацию об обработке ASCII и unicode в Python, это действительно полезный сайт: https://docs.python.org/2/howto/unicode.html

4
ответ дан 23 November 2019 в 20:45
поделиться

Я использую эту вспомогательную функцию во всех своих проектах. Если он не может преобразовать юникод, он игнорирует его. Это связано с библиотекой django, но после небольшого исследования вы можете обойти это.

from django.utils import encoding

def convert_unicode_to_string(x):
    """
    >>> convert_unicode_to_string(u'ni\xf1era')
    'niera'
    """
    return encoding.smart_str(x, encoding='ascii', errors='ignore')

После этого я больше не получаю никаких ошибок Unicode.

23
ответ дан 23 November 2019 в 20:45
поделиться

Вы написали "" "Я предполагаю, что это означает, что HTML содержит некоторую неправильно сформированную попытку юникода где-то." ""

Не ожидается, что HTML будет содержать какую-либо "попытку юникода", правильно сформированную или нет. Он обязательно должен содержать символы Unicode, закодированные в некоторой кодировке, которая обычно предоставляется заранее ... ищите "charset".

Похоже, вы предполагаете, что кодировка - UTF-8 ... на каком основании? Байт "\ xA0", который отображается в вашем сообщении об ошибке, указывает на то, что у вас может быть однобайтовая кодировка, например cp1252.

Если вы не можете понять смысл объявления в начале HTML, попробуйте использовать chardet , чтобы узнать, какова вероятная кодировка.

Почему вы пометили свой вопрос с помощью «регулярного выражения»?

Обновите после того, как вы заменили весь вопрос на не вопрос:

html = urllib.urlopen(link).read()
# html refers to a str object. To get unicode, you need to find out
# how it is encoded, and decode it.

html.encode("utf8","ignore")
# problem 1: will fail because html is a str object;
# encode works on unicode objects so Python tries to decode it using 
# 'ascii' and fails
# problem 2: even if it worked, the result will be ignored; it doesn't 
# update html in situ, it returns a function result.
# problem 3: "ignore" with UTF-n: any valid unicode object 
# should be encodable in UTF-n; error implies end of the world,
# don't try to ignore it. Don't just whack in "ignore" willy-nilly,
# put it in only with a comment explaining your very cogent reasons for doing so.
# "ignore" with most other encodings: error implies that you are mistaken
# in your choice of encoding -- same advice as for UTF-n :-)
# "ignore" with decode latin1 aka iso-8859-1: error implies end of the world.
# Irrespective of error or not, you are probably mistaken
# (needing e.g. cp1252 or even cp850 instead) ;-)
5
ответ дан 23 November 2019 в 20:45
поделиться
>>> u'aあä'.encode('ascii', 'ignore')
'a'

Расшифруйте возвращаемую строку, используя кодировку в соответствующем метатеге в ответе или в Заголовок Content-Type , затем кодируйте.

Метод encode (кодирование, ошибки) принимает пользовательские обработчики ошибок. Значения по умолчанию, помимо игнорировать , следующие:

>>> u'aあä'.encode('ascii', 'replace')
b'a??'
>>> u'aあä'.encode('ascii', 'xmlcharrefreplace')
b'a&#12354;&#228;'
>>> u'aあä'.encode('ascii', 'backslashreplace')
b'a\\u3042\\xe4'

См. https://docs.python.org/3/library/stdtypes.html#str.encode

209
ответ дан 23 November 2019 в 20:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: