Преобразование Unicode в ASCII без ошибок в Python

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

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

но я получаю 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 где-то. могу ли я просто удалить все байты кода, вызывающие проблему, вместо того, чтобы получать ошибку?

11 ответов


обновить 2018:

по состоянию на февраль 2018 года, используя сжатия, как gzip стало довольно популярны (около 73% всех веб-сайтов используют его, включая крупные сайты, такие как Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow и сетевые сайты Stack Exchange).
Если вы выполните простое декодирование, как в исходном ответе, с ответом 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

этот код считывает ответ и помещает байты в буфер. The gzip модуль затем считывает буфер с помощью


>>> u'aあä'.encode('ascii', 'ignore')
'a'

EDIT:

Декодируйте строку, которую вы получаете обратно, используя либо кодировку в соответствующем meta тег в ответе или в Content-Type заголовок, потом кодировать.

метод encode() принимает другие значения как "игнорировать". Например: 'replace',' xmlcharrefreplace','backslashreplace'. См.https://docs.python.org/3/library/stdtypes.html#str.encode


в качестве дополнения к ответу Игнасио Васкеса-Абрамса

>>> u'aあä'.encode('ascii', 'ignore')
'a'

иногда желательно удалить акценты с символов и распечатать базовую форму. Это можно сделать с помощью

>>> import unicodedata
>>> unicodedata.normalize('NFKD', u'aあä').encode('ascii', 'ignore')
'aa'

вы также можете перевести другие символы (например, знаки препинания) в их ближайшие эквиваленты, например, правая одинарная кавычка символ Юникода не преобразуется в ASCII-Апостроф при кодировании.

>>> print u'\u2019'
’
>>> unicodedata.name(u'\u2019')
'RIGHT SINGLE QUOTATION MARK'
>>> u'\u2019'.encode('ascii', 'ignore')
''
# Note we get an empty string back
>>> u'\u2019'.replace(u'\u2019', u'\'').encode('ascii', 'ignore')
"'"

хотя есть более эффективные способы достижения этой цели. См. этот вопрос для получения более подробной информации где находится база данных Python "лучший ASCII для этого Unicode"?


использовать unidecode - он даже мгновенно преобразует странные символы в ascii и даже преобразует китайский язык в фонетический ascii.

$ pip install unidecode

затем:

>>> from unidecode import unidecode
>>> unidecode(u'北京')
'Bei Jing'
>>> unidecode(u'Škoda')
'Skoda'

Я использую эту вспомогательную функцию во всех моих проектах. Если он не может преобразовать unicode, он игнорирует его. Это связано с библиотекой 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 после использования этого.


для сломанных консолей, таких как cmd.exe и вывод HTML вы всегда можете использовать:

my_unicode_string.encode('ascii','xmlcharrefreplace')

это сохранит все символы не-ascii, делая их печатными в чистом ASCII и в HTML.

предупреждение: если вы используете это в рабочем коде, чтобы избежать ошибок, то скорее всего что-то неправильно в коде. Единственным допустимым вариантом использования для этого является печать на консоль не unicode или простое преобразование в HTML-объекты в контекст HTML.

и, наконец, если вы находитесь в windows и используете cmd.exe, то вы можете ввести chcp 65001 для включения вывода utf-8 (работает с консольным шрифтом Lucida). Возможно, вам придется добавить myUnicodeString.encode('utf8').


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

HTML не должен содержать никаких "попыток unicode", хорошо сформированных или нет. Он должен обязательно содержать символы Юникода, закодированные в некоторой кодировке, которая обычно поставляется спереди ... ищите "charset".

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

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

почему вы пометили свой вопрос "regex"?

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

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) ;-)

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

line = 'my big string'

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

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


Я думаю, что ответ есть, но только в битах и кусках, что затрудняет быстрое решение проблемы, такой как

UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 2818: ordinal not in range(128)

давайте возьмем пример, Предположим, что у меня есть файл, который имеет некоторые данные в следующей форме (содержащие символы ascii и не ascii )

1/10/17, 21:36 - земле : добро пожаловать ��

и мы хотим игнорировать и сохранять только символы ascii.

этот код будет делать:

import unicodedata
fp  = open(<FILENAME>)
for line in fp:
    rline = line.strip()
    rline = unicode(rline, "utf-8")
    rline = unicodedata.normalize('NFKD', rline).encode('ascii','ignore')
    if len(rline) != 0:
        print rline

и тип (rline) будет дать тебе

>type(rline) 
<type 'str'>

unicodestring = '\xa0'

decoded_str = unicodestring.decode("windows-1252")
encoded_str = decoded_str.encode('ascii', 'ignore')

для меня


Похоже, вы используете python 2.икс. Python 2.x по умолчанию используется ascii, и он не знает о Unicode. Отсюда и исключение.

просто вставьте нижеприведенную строку после shebang, она будет работать

# -*- coding: utf-8 -*-