UnicodeEncodeError: 'ascii' codec can't encode character u'xa0' in position 20: ordinal not in range(128)

Mam problemy z obsługą znaków unicode z tekstu pobranego z różnych stron internetowych (na różnych stronach). Używam BeautifulSoup.

Problem polega na tym, że błąd nie zawsze jest powtarzalny; czasami działa z niektórymi stronami, a czasami, barfuje rzucając UnicodeEncodeError. Próbowałem już wszystkiego, co przychodzi mi do głowy, a mimo to nie znalazłem niczego, co działałoby konsekwentnie bez rzucania jakiegoś błędu związanego z Unicode.

Jeden z fragmentów kodu, który powoduje problemy jest pokazany poniżej:

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

Oto ślad stosu utworzony na niektórych ciągach, gdy powyższy fragment jest uruchamiany:

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)

Podejrzewam, że dzieje się tak dlatego, że niektóre strony (a dokładniej strony z niektórych witryn) mogą być zakodowane, podczas gdy inne mogą być niekodowane. Wszystkie strony są zlokalizowane w Wielkiej Brytanii i dostarczają danych przeznaczonych do konsumpcji w Wielkiej Brytanii - nie ma więc problemów związanych z internalizacją lub radzeniem sobie z tekstem napisanym w języku innym niż angielski.

Czy ktoś ma jakieś pomysły, jak to rozwiązać, abym mógł KONSEKWENTNIE naprawić ten problem?

Rozwiązanie

Musisz przeczytać Python Unicode HOWTO. Ten błąd jest bardzo pierwszym przykładem.

Zasadniczo, przestań używać str do konwersji z unicode na zakodowany tekst / bajty.

Zamiast tego poprawnie użyj .encode(), aby zakodować ciąg znaków:

p.agent_info = u' '.join((agent_contact, agent_telno)).encode('utf-8').strip()

lub pracować całkowicie w unicode.

Komentarze (18)

Jest to klasyczny punkt bólu python unicode! Rozważ następujące:

a = u'bats\u00E0'
print a
 => batsà

Wszystko dobrze do tej pory, ale jeśli zadzwonimy do str(a), zobaczmy co się stanie:

str(a)
Traceback (most recent call last):
  File "", line 1, in 
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)

Oh dip, to'nie zrobi nikomu nic dobrego! Aby naprawić błąd, zakoduj bajty jawnie za pomocą .encode i powiedz pythonowi, jakiego kodeka ma użyć:

a.encode('utf-8')
 => 'bats\xc3\xa0'
print a.encode('utf-8')
 => batsà

Voil\u00E0!

Problem polega na tym, że kiedy wywołujesz str(), python używa domyślnego kodowania znaków, aby spróbować zakodować bajty, które mu podałeś, które w twoim przypadku są czasami reprezentacjami znaków unicode. Aby rozwiązać ten problem, musisz powiedzieć pythonowi, jak ma postępować z podanym ciągiem znaków, używając .encode('whatever_unicode'). Przez większość czasu, powinieneś być w porządku używając utf-8.

Doskonałą prezentację tego tematu można znaleźć w wykładzie Neda Batcheldera na PyConie: http://nedbatchelder.com/text/unipain.html

Komentarze (4)

I've actually found that in most of my cases, just stripping out those characters is much simpler:

s = mystring.decode('ascii', 'ignore')
Komentarze (7)