UnicodeEncodeError

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

Hallo,
Ich probiere mittels mechanize und html5lib (BeautifulSoup) eine Seite zu parsen die oft sehr exotische Zeichen enthält. Deutsche Umlaute und Ähnliches funktionieren alle einwandfrei nur sobald z.b so ein Konstrukt auf der Seite vorkommt:
Bild
Bekomme ich den folgenden Fehler:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
Das finde ich schon sehr merkwürdig, da ich mir eigentlich ziemlich sicher bin das html5lib automatisch alle Zeichenketten in Unicode umwandelt.
(Ich benutze den BeautifulSoup tree)

Code: Alles auswählen

BeautifulSoup = html5lib.HTMLParser(
    tree = html5lib.treebuilders.getTreeBuilder("beautifulsoup"))
Kann mir jemand helfen?
Zuletzt geändert von cryzed am Mittwoch 13. Januar 2010, 16:13, insgesamt 3-mal geändert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hallo cryzed, willkommen im Forum,

sicher kann man dir theoretisch helfen, aber lies deinen Post durch und entscheide: könnte man dir auf der Basis deines Posts irgendeine sinnvolle Antwort geben? Du postest weder deinen Code, noch einen kompletten Traceback noch eine URL zu so einer Seite die fehlschlägt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

Hallo Leonidas,
Ich dachte, ja - wenn das nicht der Fall ist so entschuldige ich mich. Der Quelltext des Scripts: http://paste.pocoo.org/show/110130/
Der betreffende HTML Quelltext aus der Seite die ich parsen möchte ist dort in der Variable reply_html definiert.

Der Traceback:
Traceback (most recent call last):
File "script.py", line 73, in <module>
main()
File "script.py", line 70, in main
print(Reply(reply).blockquote)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-17: ordinal not in range(128)
EDIT:
Ich habe das Problem gerade gelöst mittels:

Code: Alles auswählen

def main():
    soup = BeautifulSoup.parse(reply_html)
    for reply in soup.findAll("td", {"class":"reply"}):
        print((Reply(reply).blockquote).encode("utf-8"))
Die Zeichen werden zwar jetzt ausgegeben aber noch falsch dargestellt:
█▀█░█▀█░█▀█░█▀█░█▀█░█▄░█░█▀▀░░
█▀▀░█▀▄░█░█░█▀▀░█▀█░█▀██░█▀░░░
▀░░░▀░▀░▀▀▀░▀░░░▀░▀░▀░░▀░▀▀▀░▀
Sie jetzt so aus:
-„ █▀█░█▀█░█▀█░█▀█░█▀█░█▄░█░█▀▀░░ █▀▀░█▀▄░█░█░█▀▀░█▀█░█▀██░█▀░░░ ▀░░░▀░▀░▀▀▀░▀░░░▀░▀░▀░░▀░▀▀▀░▀
Warum das allerdings jetzt nur so geht ist mir rätselhaft. Da der string in Reply.blockquote ja eh schon Unicode ist warum geht es nur wenn ich ihn nochmal encode? Ich benutze übrigens Python 2.5.2 und arbeite unter Ubuntu 8.10.
BlackJack

@cryzed: Ich glaube Du vermutest Dein Problem an der falschen Stelle. `html5lib` gibt Dir in der Tat Unicode zurück, aber Unicode kann man nicht immer einfach so mit ``print`` ausgeben. Das funktioniert nur, wenn der Interpreter zufällig richtig erraten kann welche Kodierung das Programm am anderen Ende von `sys.stdout` erwartet, also zum Beispiel ein Terminal. Falls das nicht geht, musst Du selber dafür sorgen, dass aus den Unicode-Objekten irgendwie sinnvolle Bytes werden. Denn nur Bytes können ein Programm betreten oder verlassen, Unicode ist etwas abstraktes, programminternes.

Jetzt musst Du nur noch heraus finden welche Kodierung das Programm am anderen Ende der Ausgabe erwartet und hoffen, dass das die Sonderzeichen richtig anzeigen kann.
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

BlackJack, nachdem was du sagst sollte das Folgende ja eigentlich die korrekten Zeichen problemlos in eine Datei schreiben:

Code: Alles auswählen

def main():
    soup = BeautifulSoup.parse(reply_html)
    for reply in soup.findAll("td", {"class":"reply"}):
        codecs.open("test.txt", "wb", encoding = "utf-8").write(Reply(reply).blockquote)
Wenn ich die Datei "test.txt" nun mit gedit öffne sollte dieser Editor die Zeichen ziemlich sicher richtig anzeigen können - tut er aber nicht.
Habe ich was falsch verstanden?, und wenn ja, kannst du mir einen Lösungsweg zeigen?
BlackJack

@cryzed: Ist in gedit auch auf UTF-8 eingestellt? Was zeigt der Editor an?

Edit: Die Darstellung mit den vielen â's bekommt man übrigens, wenn man das UTF-8 als cp1252 dekodiert.
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

Vielen Dank für die Info BlackJack. Ich benutze momentan Editra und habe extra noch einmal unter den Optionen geguckt, es ist utf-8 eingestellt, Ich bekomme trotzdem die gleichen Zeichen angezeigt. Mit gedit habe ich die Optionen für die Kodierung erst gar nicht gefunden.

Bild
BlackJack

Tja, dann müsstest Du mal etwas Eindeutigeres zeigen. Zum Beispiel die `repr()`-Darstellung dessen was Du da von der Webseite bekommst.
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

BlackJack hat geschrieben:Tja, dann müsstest Du mal etwas Eindeutigeres zeigen. Zum Beispiel die `repr()`-Darstellung dessen was Du da von der Webseite bekommst.
So BlackJack, hier befindet sich jetzt noch einmal der ganze Source der in dem Python Script vorkommt: http://paste.pocoo.org/show/u1wRVPWW1Bm5He0ROd2V/.
Ich hole mir davon nichts von der Webseite, sondern speichere den Teil den ich parsen will in der Variable "reply_html". Ich möchte jetzt einfach wissen warum "y" in Zeile 108 immer noch ein "str"-Typ ist und warum ich in Zeile 112 den folgenden Traceback bekomme:
Traceback (most recent call last):
File "4chan.py", line 115, in <module>
main()
File "4chan.py", line 112, in main
print(z.blockquote)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-41: ordinal not in range(128)
Darauf muss es doch irgendeine Antwort geben und eine Lösung vorhanden sein.
BlackJack

@cryzed: In Zeile 107 erstellst Du ein `unicode`-Objekt und kodierst das sofort wieder in UTF-8. Natürlich ist das wieder ein `str`. Was hättest Du denn erwartet?

Und `z.blockquote` ist vom Typ `unicode` weil `BeautifulSoup` eben solche Objekte liefert. Und wenn Du das einfach so mit ``print`` ausgibst, versucht Python da ein `str` draus zu machen, im schlechtesten Fall mit ASCII als Kodierung. Wenn in der Unicode-Zeichenkette aber Zeichen enthalten sind, die sich in ASCII nicht kodieren lassen, dann gibt's einen `UnicodeEncodeError`. Alles eigentlich nicht weiter überraschend.

*Du* musst halt explizit entscheiden welche Kodierung verwendet werden soll und damit dann explizit kodieren.

Übrigens sind die Klammern, die Du bei ``print`` setzt, verwirrend. ``print`` ist in Python <3 keine Funktion sondern eine Anweisung. Da ab Python 3.0 `unicode` `str` heisst und `str` zu `bytes` geworden ist, kann das den Leser bei so etwas echt durcheinander bringen.
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

Danke, das hat so einiges geklärt. Wärst du vielleicht so nett und könntest einmal ganz kurz posten wie du es machen würdest, damit du die richtige Ausgabe von print(z.blockquote) bekommst und eventuell wie man diese auch noch richtig kodiert in eine Datei schreibt? Ich bin anscheinend einfach unfähig und bekomme es nicht hin, selbst mit dem Wissen was ich jetzt habe. Ich würde mich über einen Code-Schnipsel deinerseits sehr freuen.

Ich bekomme nämlich wenn meine main-funktion so aussieht: http://paste.pocoo.org/show/xmPveITC77B3Jx2uAwJ1/
Die folgende Ausgabe bekomme: http://paste.pocoo.org/show/gDfTERykmiLBKjNz4jM1/
Dieses Zeichengewirr am Ende ergibt aber keinen Sinn bzw. sollte da nicht stehen eigentlich nämlich müssten ja mindestens diese Zeichen:
█▀█░█▀█░█▀█░█▀█░█▀█░█▄░█░█▀▀░░
richtig ausgegeben werden, weil direkt darüber zwischen den "<str>" Ausgaben hat es ja auch geklappt.
BlackJack

Dazu müsste man zwei Fragen klären: In welcher Kodierung soll denn überhaupt geschrieben werden und "rät" `BeautifulSoup` die Kodierung von dem HTML-Fragment überhaupt richtig. Kann nämlich durchaus auch sein, dass die Anhaltspunkte dafür nicht ausreichen und `BeautifulSoup` denkt, das sei ISO-8859-1. Etwas was bei einer kompletten Webseite nicht so leicht passiert, weil die oft die Kodierungsangabe in einem `<meta>`-Tag stehen haben.
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

BlackJack hat geschrieben:Dazu müsste man zwei Fragen klären: In welcher Kodierung soll denn überhaupt geschrieben werden und "rät" `BeautifulSoup` die Kodierung von dem HTML-Fragment überhaupt richtig. Kann nämlich durchaus auch sein, dass die Anhaltspunkte dafür nicht ausreichen und `BeautifulSoup` denkt, das sei ISO-8859-1. Etwas was bei einer kompletten Webseite nicht so leicht passiert, weil die oft die Kodierungsangabe in einem `<meta>`-Tag stehen haben.
Auf der Seite die ich parsen will steht auf jeder Seite das im <head>:
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=UTF-8">
Geschrieben werden soll in UTF-8, also die Kodierung mit der auch das HTML Dokument der Seite in meinem Browser (Mozilla Firefox) angezeigt wird. Ich habe den Code jetzt also folgendermaßen geändert: http://paste.pocoo.org/show/G9lEyGi2v2oSwp8ftPcf/. Jetzt bekomme ich eine andere Ausgabe, welche aber immer noch unkorrekt ist: http://paste.pocoo.org/show/Xjptq1Nn3Xa9r9M2AkpG/

Vielen Dank für all deine Geduld und Hilfe.
Zuletzt geändert von cryzed am Mittwoch 13. Januar 2010, 16:16, insgesamt 1-mal geändert.
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

Gerade was herausgefunden: http://paste.pocoo.org/show/zpahw0eMitm1RawlRUdJ/
So funktioniert es, ist aber jetzt sicher nicht mit allem kompatibel, oder?
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Wenn du eine Erklärung für Zeile 6 haben willst: in Python 3 gibt es keine Unterscheidung mehr zwischen Bytestring und Unicode-Objekt. Es gibt nur noch Strings. Siehe auch: http://docs.python.org/3.0/whatsnew/3.0 ... e-vs-8-bit
Benutzeravatar
cryzed
User
Beiträge: 82
Registriert: Samstag 28. März 2009, 15:53

So, Ich bedanke mich viele viele Male, vor allem bei dir BlackJack. Wenn ich jedes Unicode Objekt einfach mit UTF-8 encode läuft das Script komplett ohne Fehler durch, nur so ganz spezielle Zeichen machen dann ab und an gerne Probleme. Ich müsste jetzt noch eine Möglichkeit finden herauszufinden ob sich Zeichen auf der Seite befinden die zwar vom Firefox richtig dargestellt werden aber nicht im <meta> charset deklariert sind.
BlackJack

@derdon: Das hat nichts mit Python 3.0 zu tun, da gibt es auch den Namen `unicode` nicht mehr in den "builtins".

@cryzed: Das ist übles rumgehacke an Symptomen und keine Problemlösung. Ich denke das Problem ist die `_strip_html()`-Funktion, denn da muss BeautifulSoup wieder ohne Hinweise raten was denn wohl die Kodierung des HTML-Fragments sein mag. Warum machst Du das überhaupt so umständlich? BeautifulSoup nach `str` mit HTML nach BeautifulSoup und dann nach `unicode`, und das alles nur um die Tags zu entfernen!? Das kann man auch etwas direkter haben.

Wenn man eine Unicode-Zeichenkette mit cp1252 kodiert und dabei UTF-8 heraus bekommt, dann ist das Programm fehlerhaft.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Also ich hätte ja schon *längst* lxml genommen, denn das BeautifulSoup-rumgehacke wäre mir dann irgendwann auch zu blöd geworden. Außerdem hätte ich durchweg Unicode verwendet statt da Bytestrings und Unicodestrings zusammenzumischen, was immer zu fürchterlichem Code führt.

Und außerdem Frage ich micht, warum man sachen im Pastebin als "privat" markiert und dann in ein öffentliches Forum stellt. Das ist wie Flüstern in ein Megaphon.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Welches "BeautifulSoup-rumgehacke"!? Das ist ja nun echt Geschmackssache.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Bei mir haben die Selektoren bei BeautifulSoup öfter mal rumgesponnen und musste den Autor um Rat fragen, der mir dann eine ganz komische Lösung geliefert hat. Außerdem finde ich es eh nützlicher, dass wenn man schon einen HTML5 Parser nutzt, dass dann BeautifulSoup keine speziellen Vorteile[*] ggü. anderen Libraries hat.

[*] Außer dass sie halt Python 3 kompatibel ist, das muss man zugeben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten