Python: конвертировать Unicode в ASCII без ошибок
Мой код просто сбрасывает веб-страницу, а затем преобразует ее в Юникод.
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. Могу ли я просто отказаться от каких-либо кодовых байтов, вызывающих проблему, вместо получения ошибки?
Ответы
Ответ 1
Можем ли мы получить фактическое значение, используемое для 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" - это то, что я использовал в качестве примера. Я получил это от чарта, и у него было 0,5 уверенности, что это правильно! (ну, как указано в строке длиной 1 символа, что вы ожидаете). Вы должны изменить это на кодировку строки байта, возвращаемой из .urlopen().read()
, на то, что относится к полученному вами контенту.
Другая проблема, я вижу, что метод string .encode()
возвращает измененную строку и не изменяет исходный код на месте. Так что бесполезно иметь self.response.out.write(html)
, поскольку html не является закодированной строкой из html.encode(если это то, к чему вы изначально стремились).
Как предложил Игнасио, проверьте исходную веб-страницу для фактического кодирования возвращаемой строки из read()
. Это либо в одном из метатегов, либо в заголовке ContentType в ответе. Используйте это как параметр для .decode()
.
Помните, однако, что не следует полагать, что другие разработчики достаточно ответственны, чтобы убедиться, что объявления заголовка заголовка и/или метасимвола соответствуют фактическому содержимому. (Что такое PITA, да, я должен знать, я был одним из них раньше).
Ответ 2
>>> u'aあä'.encode('ascii', 'ignore')
'a'
EDIT:
Расшифруйте возвращаемую строку, используя либо кодировку в соответствующем теге meta
в ответе, либо в заголовке Content-Type
, а затем закодируйте.
Метод encode()
принимает другие значения как "игнорировать". Например: "заменить", "xmlcharrefreplace", "backslashreplace". См. https://docs.python.org/3/library/stdtypes.html#str.encode
Ответ 3
В качестве продолжения ответа Игнасио Васкеса-Абрамса
>>> u'aあä'.encode('ascii', 'ignore')
'a'
Иногда желательно удалить акценты с символов и распечатать базовую форму. Это можно сделать с помощью
>>> import unicodedata
>>> unicodedata.normalize('NFKD', u'aあä').encode('ascii', 'ignore')
'aa'
Вы также можете перевести другие символы (например, пунктуацию) в их ближайшие эквиваленты, например символ юникода RIGHT SINGLE QUOTATION MARK не преобразуется в ascii APOSTROPHE при кодировании.
>>> 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')
"'"
Хотя есть более эффективные способы этого. См. Этот вопрос для более подробной информации Где находится лучший ASCII Python для этого Unicode " базы данных?
Ответ 4
Используйте unidecode - он даже мгновенно превращает странные персонажи в ascii и даже преобразует китайцев в фонетический ascii.
$ pip install unidecode
то
>>> from unidecode import unidecode
>>> unidecode(u'北京')
'Bei Jing'
>>> unidecode(u'Škoda')
'Skoda'
Ответ 5
Я использую эту вспомогательную функцию во всех моих проектах. Если он не может преобразовать Юникод, он игнорирует его. Это связано с библиотекой 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-ошибок после использования.
Ответ 6
Для сломанных консолей типа cmd.exe
и вывода HTML вы всегда можете использовать:
my_unicode_string.encode('ascii','xmlcharrefreplace')
Это сохранит все символы не-ascii, делая их пригодными для печати в чистом ASCII и HTML.
ПРЕДУПРЕЖДЕНИЕ. Если вы используете это в производственном коде, чтобы избежать ошибок, скорее всего, в вашем коде есть что-то не так. Единственный допустимый прецедент для этого - печать на консоли без юникода или простое преобразование в HTML-объекты в контексте HTML.
И, наконец, если вы работаете с окнами и используете cmd.exe, вы можете ввести chcp 65001
, чтобы включить вывод utf-8 (работает с шрифтом Lucida Console). Возможно, вам потребуется добавить myUnicodeString.encode('utf8')
.
Ответ 7
Вы написали "" Я предполагаю, что это означает, что HTML содержит некорректную попытку в юникоде где-то. ""
В 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) ;-)
Ответ 8
Если у вас есть строка line
, вы можете использовать метод .encode([encoding], [errors='strict'])
для строк для преобразования типов кодирования.
line = 'my big string'
line.encode('ascii', 'ignore')
Для получения дополнительных сведений об обработке ASCII и unicode в Python это действительно полезный сайт: https://docs.python.org/2/howto/unicode.html
Ответ 9
Я думаю, что ответ есть, но только в битах и кусках, что затрудняет быстрое исправление проблемы, например
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 2818: ordinal not in range(128)
Возьмем пример: предположим, что у меня есть файл, который имеет некоторые данные в следующем виде (содержащий символы ascii и non-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'>
Ответ 10
unicodestring = '\xa0'
decoded_str = unicodestring.decode("windows-1252")
encoded_str = decoded_str.encode('ascii', 'ignore')
Работает для меня
Ответ 11
Похоже, вы используете python 2.x.
Python 2.x по умолчанию соответствует ascii, и он не знает о Unicode. Отсюда исключение.
Просто вставьте нижнюю строку после shebang, она будет работать
# -*- coding: utf-8 -*-