it-swarm.com.de

Konvertieren Sie Unicode nach ASCII ohne Fehler in Python

Mein Code kratzt nur eine Webseite und konvertiert sie dann in Unicode.

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

Aber ich bekomme ein 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)

Ich gehe davon aus, dass das HTML irgendwo einen falsch formulierten Unicode-Versuch enthält. Kann ich einfach die Code-Bytes löschen, die das Problem verursachen, anstatt einen Fehler zu erhalten?

167
themirror

Update 2018:

Seit Februar 2018 ist die Verwendung von Komprimierungen wie gzipsehr beliebt (ca. 73% aller Websites verwenden sie, einschließlich großer Websites wie Google, YouTube, Yahoo, Wikipedia, Reddit, Stack Overflow und Stack Exchange Network Websites).
Wenn Sie eine einfache Dekodierung wie in der ursprünglichen Antwort mit einer gezippten Antwort durchführen, erhalten Sie eine Fehlermeldung wie die folgende oder eine ähnliche:

UnicodeDecodeError: Der Codec 'utf8' kann das Byte 0x8b an Position 1 nicht decodieren: Unerwartetes Codebyte

Um eine gzpipped-Antwort zu dekodieren, müssen Sie die folgenden Module hinzufügen (in Python 3):

_import gzip
import io
_

Hinweis: In Python 2 würden Sie StringIO anstelle von io verwenden

Dann können Sie den Inhalt wie folgt analysieren:

_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
_

Dieser Code liest die Antwort und legt die Bytes in einem Puffer ab. Das Modul gzip liest dann den Puffer mit der Funktion GZipFile. Danach kann die gezippte Datei wieder in Bytes eingelesen und am Ende zu normal lesbarem Text decodiert werden.

Ursprüngliche Antwort von 2010:

Können wir den tatsächlichen Wert ermitteln, der für link verwendet wird?

Außerdem tritt dieses Problem hier normalerweise auf, wenn wir versuchen, eine bereits codierte Byte-Zeichenfolge zu .encode(). Sie könnten also versuchen, es zuerst wie in zu dekodieren

_html = urllib.urlopen(link).read()
unicode_str = html.decode(<source encoding>)
encoded_str = unicode_str.encode("utf8")
_

Als Beispiel:

_html = '\xa0'
encoded_str = html.encode("utf8")
_

Scheitert mit

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

Während:

_html = '\xa0'
decoded_str = html.decode("windows-1252")
encoded_str = decoded_str.encode("utf8")
_

Gelingt ohne Fehler. Beachten Sie, dass "windows-1252" etwas ist, das ich als Beispiel verwendet habe. Ich habe das von chardet bekommen und es hatte 0,5 Gewissheit, dass es richtig ist! (Nun, wie bei einer Zeichenfolge mit einer Länge von 1 Zeichen zu erwarten). Sie sollten dies in die Codierung der von .urlopen().read() zurückgegebenen Byte-Zeichenfolge ändern, um festzustellen, was für den abgerufenen Inhalt gilt.

Ein weiteres Problem, das ich dort sehe, ist, dass die Zeichenfolgenmethode .encode() die geänderte Zeichenfolge zurückgibt und die Quelle nicht an Ort und Stelle ändert. Es ist also nutzlos, self.response.out.write(html) zu haben, da html nicht die codierte Zeichenfolge aus html.encode ist (wenn Sie das ursprünglich wollten).

Überprüfen Sie, wie von Ignacio vorgeschlagen, die Quellwebseite auf die tatsächliche Codierung der von read() zurückgegebenen Zeichenfolge. Es befindet sich entweder in einem der Meta-Tags oder im ContentType-Header in der Antwort. Verwenden Sie das dann als Parameter für .decode().

Beachten Sie jedoch, dass nicht davon ausgegangen werden sollte, dass andere Entwickler verantwortlich genug sind, um sicherzustellen, dass die Header- und/oder Metazeichensatzdeklarationen mit dem tatsächlichen Inhalt übereinstimmen. (Was ist ein PITA, ja, ich sollte wissen, ich war einer von denen vor).

100
Vin-G
>>> u'aあä'.encode('ascii', 'ignore')
'a'

EDIT:

Dekodieren Sie die zurückgegebene Zeichenfolge, indem Sie entweder den Zeichensatz im entsprechenden Tag meta in der Antwort oder im Header Content-Type Verwenden und dann kodieren.

Die Methode encode() akzeptiert andere Werte als "ignore". Zum Beispiel: 'replace', 'xmlcharrefreplace', 'backslashreplace'. Siehe https://docs.python.org/3/library/stdtypes.html#str.encode

202

Als Erweiterung zu Ignacio Vazquez-Abrams Antwort

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

Manchmal ist es wünschenswert, Akzente aus den Zeichen zu entfernen und das Basisformular auszudrucken. Dies kann erreicht werden mit

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

Möglicherweise möchten Sie auch andere Zeichen (z. B. Satzzeichen) in ihre nächsten Entsprechungen übersetzen. Beispielsweise wird das Unicode-Zeichen RIGHT SINGLE QUOTATION MARK beim Codieren nicht in ein ASCII-APOSTROPHE konvertiert.

>>> 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')
"'"

Es gibt zwar effizientere Möglichkeiten, dies zu erreichen. Weitere Informationen finden Sie in dieser Frage Wo ist Pythons "beste ASCII für diese Unicode" -Datenbank?

114
Peter Gibson

Verwenden Sie nidecode - es konvertiert sogar seltsame Zeichen sofort in ASCII und konvertiert sogar Chinesisch in phonetisches ASCII.

$ pip install unidecode

dann:

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

Ich benutze diese Hilfsfunktion in all meinen Projekten. Wenn der Unicode nicht konvertiert werden kann, wird er ignoriert. Dies knüpft an eine Django Bibliothek an, aber mit ein wenig Recherche könnten Sie es umgehen.

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

Ich bekomme keine Unicode-Fehler mehr, wenn ich das benutze.

23
Gattster

Für defekte Konsolen wie cmd.exe Und HTML-Ausgaben können Sie immer Folgendes verwenden:

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

Auf diese Weise bleiben alle Nicht-ASCII-Zeichen erhalten, während sie in reinem ASCII und in HTML gedruckt werden können.

WARNING : Wenn Sie dies im Produktionscode verwenden, um Fehler zu vermeiden, liegt höchstwahrscheinlich ein Fehler in Ihrem Code vor . Der einzig gültige Anwendungsfall hierfür ist das Drucken auf einer Nicht-Unicode-Konsole oder die einfache Konvertierung in HTML-Entitäten in einem HTML-Kontext.

Wenn Sie Windows verwenden und cmd.exe verwenden, können Sie chcp 65001 Eingeben, um die Ausgabe von utf-8 zu aktivieren (funktioniert mit der Lucida Console-Schriftart). Möglicherweise müssen Sie myUnicodeString.encode('utf8') hinzufügen.

10
ccpizza

Sie haben "" geschrieben. Ich nehme an, dass das HTML irgendwo einen falsch geformten Unicode-Versuch enthält. ""

Es wird NICHT erwartet, dass der HTML-Code irgendeine Art von "Unicode-Versuch" enthält, wohlgeformt oder nicht. Es muss notwendigerweise Unicode-Zeichen enthalten, die in einer Codierung codiert sind, die normalerweise im Voraus geliefert wird. Suchen Sie nach "charset".

Sie scheinen anzunehmen, dass der Zeichensatz UTF-8 ist ... aus welchen Gründen? Das in Ihrer Fehlermeldung angezeigte Byte "\ xA0" weist darauf hin, dass Sie möglicherweise einen Einzelbyte-Zeichensatz haben, z. cp1252.

Wenn die Deklaration am Anfang des HTML-Codes keinen Sinn ergibt, versuchen Sie, mit chardet die wahrscheinliche Kodierung zu ermitteln.

Warum haben Sie Ihre Frage mit "Regex" markiert?

Aktualisieren Sie , nachdem Sie Ihre gesamte Frage durch eine Nicht-Frage ersetzt haben:

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) ;-)
5
John Machin

Wenn Sie eine Zeichenfolge line haben, können Sie die Methode .encode([encoding], [errors='strict']) für Zeichenfolgen verwenden, um Codierungstypen zu konvertieren.

line = 'my big string'

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

Weitere Informationen zum Umgang mit ASCII und Unicode in Python finden Sie auf der folgenden Website: https://docs.python.org/2/howto/unicode.html =

4
Jama22

Ich denke, die Antwort ist da, aber nur in Teilen, was es schwierig macht, das Problem schnell zu beheben, wie z

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

Nehmen wir ein Beispiel: Angenommen, ich habe eine Datei mit Daten in der folgenden Form (die ASCII- und Nicht-ASCII-Zeichen enthält).

10.01.17, 21:36 - Land: Willkommen ��

und wir wollen nur ASCII-Zeichen ignorieren und beibehalten.

Dieser Code reicht aus für:

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

und Typ (rline) wird Ihnen geben

>type(rline) 
<type 'str'>
4
Somum
unicodestring = '\xa0'

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

Funktioniert bei mir

1
HimalayanCoder