Konwertuj Unicode na ASCII bez błędów w Pythonie
Mój kod po prostu zeskrobuje stronę internetową, a następnie konwertuje ją do Unicode.
html = urllib.urlopen(link).read()
html.encode("utf8","ignore")
self.response.out.write(html)
Ale dostaję 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)
Zakładam, że oznacza to, że HTML zawiera gdzieś błędnie uformowaną próbę Unicode. Czy mogę po prostu porzucić bajty kodu, które powodują problem, zamiast uzyskać błąd?
11 answers
[[18]}2018 aktualizacja:
Od lutego 2018 r. używanie kompresji, takich jak gzip
, stało się dość popularne (korzysta z niego około 73% wszystkich stron internetowych, w tym duże witryny, takie jak Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow i Stack Exchange Network sites).
Jeśli wykonasz proste dekodowanie, jak w oryginalnej odpowiedzi z odpowiedzią gzipped, otrzymasz błąd podobny lub podobny do tego: {]}
UnicodeDecodeError: kodek 'utf8' nie może dekodować bajtu 0x8b w pozycji 1: nieoczekiwany bajt kodu
Aby zdekodować odpowiedź GZP należy dodać następujące moduły (w Pythonie 3):
import gzip
import io
Uwaga: w Pythonie 2 używasz StringIO
zamiast io
Następnie możesz przetworzyć zawartość w następujący sposób:
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
Ten kod odczytuje odpowiedź i umieszcza bajty w buforze. Moduł gzip
odczytuje następnie bufor za pomocą funkcji GZipFile
. Następnie plik gzipped może być ponownie wczytany do bajtów i dekodowany do normalnie czytelnego tekstu na końcu.
Oryginalna odpowiedź z 2010 roku:
Czy możemy uzyskać rzeczywistą wartość użytą dla link
?
Ponadto, zwykle napotykamy ten problem tutaj, gdy próbujemy .encode()
już zakodowany łańcuch bajtów. Więc możesz spróbować najpierw go odkodować jak w
html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")
Jako przykład:
html = '\xa0'
encoded_str = html.encode("utf8")
Fails with
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 0: ordinal not in range(128)
While:
html = '\xa0'
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")
Udaje się bez błędu. Należy pamiętać ,że "windows-1252" jest czymś, co używany jako przykład . Mam to od chardet i miał 0,5 pewności, że to prawda! (cóż, jak podano z ciągiem o długości 1 znaków, czego oczekujesz) powinieneś zmienić to na kodowanie ciągu bajtów zwróconego z .urlopen().read()
na to, co odnosi się do pobranej zawartości.
Inny problem widzę tam jest to, że metoda .encode()
string zwraca zmodyfikowany łańcuch i nie modyfikuje źródła w miejscu. Więc to trochę bezużyteczne mieć self.response.out.write(html)
jako html nie jest zakodowanym ciągiem z html.koduj (jeśli to jest to, do czego pierwotnie dążyłeś).
Jak zasugerował Ignacio, sprawdź na stronie źródłowej kodowanie zwracanego ciągu znaków z read()
. Znajduje się w jednym ze znaczników Meta lub w nagłówku ContentType w odpowiedzi. Użyj tego jako parametru dla .decode()
.
Należy jednak pamiętać, że nie należy zakładać, że inni programiści są na tyle odpowiedzialni, aby upewnić się, że nagłówek i / lub meta zestaw znaków deklaracje są zgodne z rzeczywistą treścią. (Co jest PITA, tak, powinienem wiedzieć, ja był jeden z nich wcześniej).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-24 17:25:50
>>> u'aあä'.encode('ascii', 'ignore')
'a'
EDIT:
Dekoduj otrzymany ciąg znaków, używając zestawu znaków w odpowiednim znaczniku meta
w odpowiedzi lub w nagłówku Content-Type
, a następnie Zakoduj.
Metoda encode()
przyjmuje inne wartości jako "ignoruj". Na przykład: 'replace', 'xmlcharrefreplace', 'backslashreplace'. Zobacz https://docs.python.org/3/library/stdtypes.html#str.encode
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-28 08:31:13
Jako rozszerzenie do odpowiedzi Ignacio Vazqueza-Abramsa
>>> u'aあä'.encode('ascii', 'ignore')
'a'
Czasami pożądane jest usunięcie akcentów ze znaków i wydrukowanie formy bazowej. Można to osiągnąć za pomocą
>>> import unicodedata
>>> unicodedata.normalize('NFKD', u'aあä').encode('ascii', 'ignore')
'aa'
Możesz również chcieć przetłumaczyć inne znaki (takie jak interpunkcja) na ich najbliższe odpowiedniki, na przykład prawy pojedynczy cudzysłów znak unicode nie jest konwertowany do apostrofu ascii podczas kodowania.
>>> 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')
"'"
Chociaż są bardziej efektywne sposoby na / align = "left" / Zobacz to pytanie po więcej szczegółów gdzie jest baza danych Pythona "najlepsze ASCII dla tego Unicode"?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:11
Użycie unidecode - to nawet konwertuje dziwne znaki do ascii natychmiast, a nawet konwertuje chiński do fonetycznego ascii.
$ pip install unidecode
Wtedy:
>>> from unidecode import unidecode
>>> unidecode(u'北京')
'Bei Jing'
>>> unidecode(u'Škoda')
'Skoda'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-27 18:26:09
Używam tej funkcji pomocnika we wszystkich moich projektach. Jeśli nie może przekonwertować unicode, ignoruje go. Łączy się to z biblioteką django, ale przy odrobinie badań możesz ją ominąć.
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')
Nie dostaję już żadnych błędów unicode po użyciu tego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-03 00:02:07
Dla zepsutych konsol, takich jak cmd.exe
i wyjście HTML, zawsze możesz użyć:
my_unicode_string.encode('ascii','xmlcharrefreplace')
To zachowa wszystkie znaki spoza ascii, czyniąc je drukowalnymi w czystych ASCII i W HTML.
Ostrzeżenie: Jeśli używasz tego w kodzie produkcyjnym, aby uniknąć błędów, najprawdopodobniej jest coś nie tak w Twoim kodzie . Jedynym prawidłowym przypadkiem użycia jest drukowanie na konsoli innej niż unicode lub łatwa konwersja na encje HTML w kontekście HTML.
Oraz wreszcie, jeśli jesteś w systemie windows i używasz cmd.exe następnie możesz wpisać chcp 65001
, aby włączyć wyjście utf-8(działa z czcionką Lucida Console). Może być konieczne dodanie myUnicodeString.encode('utf8')
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-22 14:20:11
Napisałeś "" zakładam, że oznacza to, że HTML zawiera gdzieś błędnie uformowaną próbę Unicode."""
Oczekuje się, że HTML nie będzie zawierał żadnego rodzaju" próby Unicode", dobrze uformowanego lub nie. Musi z konieczności zawierać znaki Unicode zakodowane w pewnym kodowaniu, które jest zwykle dostarczane z przodu ... Szukaj "charset".
Wydaje się, że zakładasz, że zestaw znaków to UTF-8 ... na jakiej podstawie? Bajt "\xa0 " pokazany w komunikacie o błędzie wskazuje, że możesz mieć kod jednobajtowy, np. cp1252.
Jeśli nie możesz uzyskać żadnego sensu z deklaracji na początku HTML, spróbuj użyć chardet , aby dowiedzieć się, jakie jest prawdopodobne kodowanie.
Dlaczego otagowałeś swoje pytanie "regex"?
Update po zastąpieniu całego pytania bez pytania:
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) ;-)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-03 03:46:55
Jeśli masz łańcuch znaków line
, możesz użyć metody .encode([encoding], [errors='strict'])
do konwersji typów kodowania.
line = 'my big string'
line.encode('ascii', 'ignore')
Aby uzyskać więcej informacji na temat obsługi ASCII i unicode w Pythonie, jest to naprawdę przydatna strona: https://docs.python.org/2/howto/unicode.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-10 07:34:48
Myślę, że odpowiedź jest tam, ale tylko w kawałkach i kawałkach, co sprawia, że trudno szybko rozwiązać problem, taki jak
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 2818: ordinal not in range(128)
Weźmy przykład, załóżmy, że mam plik, który ma pewne dane w następującej formie (zawierające znaki ascii i nie-ascii)
1/10/17, 21:36 - Ziemia: Witamy ï¿½ï ¿½
I chcemy ignorować i zachować tylko znaki ascii.
Ten kod zrobi:
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
I typ (rline) da ci
>type(rline)
<type 'str'>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-15 14:41:03
unicodestring = '\xa0'
decoded_str = unicodestring.decode("windows-1252")
encoded_str = decoded_str.encode('ascii', 'ignore')
Działa dla mnie
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-05 08:00:29
Wygląda na to, że używasz Pythona 2.x. Python 2.x domyślnie ascii i nie wie o Unicode. Stąd wyjątek.
Po prostu wklej poniższy wiersz po shebang, to będzie działać
# -*- coding: utf-8 -*-
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-03 20:04:28