UnicodeEncodeError: 'ascii' кодек не может закодировать символ u'\xa0' на позиции 20: номер не в диапазоне (128)
Я столкнулся с проблемами при работе с юникодными символами из текста, полученного с разных веб-страниц (на разных сайтах). Я использую библиотеку BeautifulSoup.
Проблема в том, что ошибка не всегда воспроизводима; иногда код работает с некоторыми страницами, а иногда выбрасывает ошибку UnicodeEncodeError
. Я перепробовал практически все возможные решения, но так и не нашел ничего, что бы работало последовательно и не выдавало ошибок, связанных с юникодом.
Вот часть кода, которая вызывает проблемы:
agent_telno = agent.find('div', 'agent_contact_number')
agent_telno = '' if agent_telno is None else agent_telno.contents[0]
p.agent_info = str(agent_contact + ' ' + agent_telno).strip()
Вот трассировка стека, которая возникает на НЕКОТОРЫХ строках, когда выполняется приведенный выше код:
Traceback (most recent call last):
File "foobar.py", line 792, in <module>
p.agent_info = str(agent_contact + ' ' + agent_telno).strip()
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 20: ordinal not in range(128)
Я подозреваю, что это связано с тем, что некоторые страницы (или, что более важно, страницы с некоторых сайтов) могут быть закодированы, в то время как другие могут быть не закодированы. Все сайты находятся в Великобритании и предоставляют данные, предназначенные для британской аудитории, поэтому проблем, связанных с интернационализацией или текстом, написанным на языках, отличных от английского, нет.
Есть ли у кого-нибудь идеи, как решить эту проблему, чтобы я мог ПОСТОЯННО исправить ее?
5 ответ(ов)
Это классическая проблема с юникодом в Python! Рассмотрим следующий пример:
a = u'bats\u00E0'
print(a)
# => batsà
Пока всё в порядке, но если мы вызовем str(a)
, давайте посмотрим, что произойдет:
str(a)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)
Ой, это не поможет никому! Чтобы исправить ошибку, явно закодируйте байты с помощью .encode
и укажите Python, какой кодек использовать:
a.encode('utf-8')
# => 'bats\xc3\xa0'
print(a.encode('utf-8'))
# => batsà
Вот и всё!
Проблема в том, что когда вы вызываете str()
, Python использует кодировку по умолчанию, чтобы попытаться закодировать байты, которые вы ему передали, а в вашем случае это иногда представления юникодных символов. Чтобы решить проблему, вы должны указать Python, как обрабатывать строку, которую вы ему передаете, используя .encode('ваш_кодек')
. В большинстве случаев вам будет достаточно использовать utf-8
.
Для превосходного объяснения этой темы посмотрите презентацию Нэда Бэтчелдера на PyCon здесь: http://nedbatchelder.com/text/unipain.html
Я нашел элегантное решение для удаления символов и сохранения строки в формате строки следующим образом:
yourstring = yourstring.encode('ascii', 'ignore').decode('ascii')
Важно отметить, что использование опции ignore
является опасным, так как оно тихо удаляет все символы Unicode (и поддержку интернационализации) в коде, который его использует. Например, если выполнить следующий код:
>>> u'City: Malmö'.encode('ascii', 'ignore').decode('ascii')
'City: Malm'
Мы видим, что строка "Malmö" преобразуется в "Malm", и символ "ö" просто отбрасывается. Будьте осторожны с использованием этого метода, если поддержка международных символов важна для вашего приложения.
Я пробовал всё, что только можно, но ничего не помогло. После того как погуглил, я нашёл следующее решение, и оно сработало. У меня используется Python 2.7.
# encoding=utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
Для того чтобы найти поддерживаемую локализацию UTF-8 в оболочке, выполните следующие шаги:
Используйте следующую команду, чтобы отобразить все доступные локали и отфильтровать только UTF-8:
locale -a | grep "UTF-8"
Экспортируйте выбранную локализацию перед выполнением скрипта. Например:
export LC_ALL=$(locale -a | grep UTF-8)
Или вы можете установить локализацию вручную:
export LC_ALL=C.UTF-8
Проверьте, работает ли это, попытавшись вывести специальный символ, например
™
:python -c 'print(u"\u2122");'
Эти шаги были протестированы в Ubuntu.
На самом деле, я обнаружил, что в большинстве случаев проще всего удалить эти символы. Вот пример кода:
s = mystring.decode('ascii', 'ignore')
Таким образом, все некорректные символы просто игнорируются, и вы получаете строку, содержащую только допустимые символы ASCII.
UnicodeDecodeError: Кодек 'charmap' не может декодировать байт X в позиции Y: символ отображается как <неопределённый>
Вызов функции модуля по его имени (строке)
Как изменить порядок столбцов в DataFrame?
Изменение типа столбца в pandas
Что такое __pycache__?