Wie Umlaute statt "komische Zeichen" anzeigen?

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
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hi!

In einem Script scrape ich Daten von Myspace und bekomme im Fall von "unbekannten" Zeichen (z.B. Umlauten) Unicode zurück - ansonsten "reine" Strings. Anstatt einem ü hab ich z.B. ü. Ich habe schon encode('utf-8').decode('latin-1') ausprobiert, aber das Ergebnis bleibt leider das selbe. Wie also kann ich die Zeichen richtig darstellen?

Das Skript: http://paste.pocoo.org/show/85526/

Ich hoffe, ihr könnt es auch nachvollziehen, ohne Myspace nutzen zu müssen. :)

Gruß

Sebastian
sea-live
User
Beiträge: 440
Registriert: Montag 18. Februar 2008, 12:24
Wohnort: RP

also das Ä1/4 kommt
hier her

Code: Alles auswählen

# -*- coding: cp1252 -*-
hate ich auch schonmal

wenn du utf-8 verwendest musst du auch u"xxxx" verwenden sonst wird das nix
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Das hier ist wirklich hilfreich (s. auch Thread weiter unten ;-) ):
[wiki]Von Umlauten, Unicode und Encodings?highlight=(umlaute)[/wiki]

Hat mich auch immer wieder beschäftigt Jahre lang - ich glaube so langsam hab ich das nun durchblickt, auch dank der Hilfe einiger Forummitglieder hier. Aber das wiki hat mir da wirklich sehr geholfen.

Sea-lives Beitrag vergisst Du lieber mal schnell ;-)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hyperion hat geschrieben:Das hier ist wirklich hilfreich (s. auch Thread weiter unten ;-) ):
[wiki]Von Umlauten, Unicode und Encodings?highlight=(umlaute)[/wiki]
Jupp. Ich hatte den Beitrag abgeschickt bevor ich mir die aktuelle Übersichtsseite angeguckt habe und daher den besagten Thread nicht gesehen.

Habe jetzt an einer Zeile etwas rumgebastelt (ergibt sich aus dem Traceback) aber ich bekomme es immer noch nicht hin:

Code: Alles auswählen

>>> import mspdat
>>> msp = mspdat.Datagetter()
>>> msp.login('abc@xyz.de', 'pw123')
>>> print msp.get_moods(pretty=True)

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    print msp.get_moods(pretty=True)
  File "C:\Python25\mspdat.py", line 57, in get_moods
    return prettify_moods(data)
  File "C:\Python25\mspdat.py", line 92, in prettify_moods
    return '\n\n'.join(s).decode('iso-8859-1')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 190-191: ordinal not in range(128)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dein Interpreter will standard mäßig ASCII ausgeben. Du bekommst aber eben Zeichen in Zeile 4 bei get_moods() zurück, die nicht in ASCII darstellbar sind. Du musst also mal gucken, dass Du beim print noch Kodierung angibst, die zur Ausgabe verwendet werden soll, also etwa so:

Code: Alles auswählen

print u"%s".encode("iso-8859-1") % msp.get_moods(pretty=True).decode("iso-8859-1")
BlackJack

@snafu: Nicht rumbasteln sondern verstehen was da passiert und gezielt handeln.

@Hyperion: Das ist ziemlich unsinnig. Du nimmst eine Unicode-Zeichenkette u'%s' und kodierst die als ISO-8859-1 ─ das ist '%s' etwas komplizierter geschrieben. Dann formatierst Du eine Unicode-Zeichenkette in eine normale Zeichenkette, was dazu führt, das die normale Zeichenkette erst einmal nach Unicode umgewandelt wird ─ aus dem '%s' wird also wieder u'%s'. *Das* kann man auch einfacher haben. :roll:

Die Frage ist, warum von `get_moods()` überhaupt eine Zeichenkette kommt und kein Unicode-Objekt. Wenn man aus den Seiten Unicode bekommt muss das ja schon mal völlig unnötigerweise umkodiert worden sein.

Wenn Textdaten das Programm betreten, sollte man sie sofort in Unicode umwandeln. `BeautifulSoup` und XML-Bibliotheken machen das schon. Innerhalb des Programms arbeitet man immer mit Unicode und erst zum spätmöglichsten Zeitpunkt, kurz bevor die Daten das Programm als Bytes verlassen müssen, wandelt man wieder entsprechend um. Alles andere ist unnötig kompliziert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Das Print braucht doch kein String-Formatting.

Letztendlich ist es so: Man bekommt einen Bytestring rein, dekodiert den in Unicode und zur Ausgabe nutzt man wieder ein Encoding, das die Konsole versteht.

Wenn das so kompliziert ist, kannst du morgen zum Usertreffen in München kommen, genau das ist das Vortragsthema.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Leider das selbe Problem:

Code: Alles auswählen

>>> print u"%s".encode("iso-8859-1") % msp.get_moods(pretty=True).decode("iso-8859-1")

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    print u"%s".encode("iso-8859-1") % msp.get_moods(pretty=True).decode("iso-8859-1")
UnicodeEncodeError: 'ascii' codec can't encode characters in position 191-192: ordinal not in range(128)
Versuche ich die Position 191-192 anzuzeigen, kommt:

Code: Alles auswählen

>>> moods = msp.get_moods()
>>> from mspdat import prettify_moods as pretty
>>> print pretty(moods)[191:192]
Ã
Vielleicht hilft das ja irgendwie...
BlackJack

Helfen würde die `repr()`-Form, damit man sieht was da *wirklich* heraus kommt und nicht wie Deine Konsole die Bytes interpretiert. Aber wie gesagt, `get_moods()` sollte Unicode oder Zeichenketten die nur ASCII enthalten zurück geben.

Und auch bei ``print`` musst Du Unicode-Objekte vorher entsprechend kodieren, damit es da keine Probleme gibt. Dass das in der Konsole meistens auf magische Weise klappt, ist keine Garantie.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

BlackJack hat geschrieben:Die Frage ist, warum von `get_moods()` überhaupt eine Zeichenkette kommt und kein Unicode-Objekt. Wenn man aus den Seiten Unicode bekommt muss das ja schon mal völlig unnötigerweise umkodiert worden sein.
Tja, das frage ich mich auch.
BlackJack hat geschrieben:Wenn Textdaten das Programm betreten, sollte man sie sofort in Unicode umwandeln. `BeautifulSoup` und XML-Bibliotheken machen das schon.
Ist es denn möglich, mit BS zu parsen und dann mit der Syntax von lxml durch den Baum zu navigieren? Ich mag nämlich die Syntax von BS nicht so und befürchte, dass ich nicht mal so auf die Schnelle etwas äquivalentes zum jetzigen Code hinbekommen mit BS-Syntax hinbekommen würde.

//edit: Okay, ich könnte natürlich den von BS geparsten Code als String ausgeben lassen und dann nochmal mit lxml drübergehen. Aber es geht doch sicher auch effizienter, oder?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

snafu hat geschrieben:Ist es denn möglich, mit BS zu parsen und dann mit der Syntax von lxml durch den Baum zu navigieren? Ich mag nämlich die Syntax von BS nicht so und befürchte, dass ich nicht mal so auf die Schnelle etwas äquivalentes zum jetzigen Code hinbekommen mit BS-Syntax hinbekommen würde.

//edit: Okay, ich könnte natürlich den von BS geparsten Code als String ausgeben lassen und dann nochmal mit lxml drübergehen. Aber es geht doch sicher auch effizienter, oder?
Und wozu das ganze? html5lib und lxml.html existieren.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben: @Hyperion: Das ist ziemlich unsinnig. Du nimmst eine Unicode-Zeichenkette u'%s' und kodierst die als ISO-8859-1 ─ das ist '%s' etwas komplizierter geschrieben. Dann formatierst Du eine Unicode-Zeichenkette in eine normale Zeichenkette, was dazu führt, das die normale Zeichenkette erst einmal nach Unicode umgewandelt wird ─ aus dem '%s' wird also wieder u'%s'. *Das* kann man auch einfacher haben. :roll:
Hm ... ok ... ich versuche das mal grad nachzuvollziehen.
wiki hat geschrieben: Wenn man einen Text mit Umlauten an die Konsole (in unserem Fall STDOUT) schicken möchte, dann muss man diesen Text in das Encoding der Konsole umwandeln.
get_moods() liefert also def. einen Bytestring zurück, vermutlich iso-8859-1 encodiert. Die Konsole meckert wegen Zeichen, die die ASCII-Kodierung nicht darstellen kann. Also muss man doch print mitteilen, dass man kein ASCII sondern, iso-8859-1 Text ausgeben will. Ich dachte immer über ein

Code: Alles auswählen

u"irgend ein Text".encode("iso-8859-1")
kann ich das machen. Aber da irre ich wohl. Wie sieht denn hier ein gangbarer Weg aus? Wenn ich BlackJack richtig verstanden habe, so bewirkt der "%"-Operator ein implizites umkodieren in das Byteformat des Bytestrings?
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hyperion hat geschrieben:Wenn ich BlackJack richtig verstanden habe, so bewirkt der "%"-Operator ein implizites umkodieren in das Byteformat des Bytestrings?
%s ist einfach nur Teil des String-Formattings, was du dir - wie schon gesagt wurde - aber auch sparen kannst.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

repr(pretty(moods)) liefert übrigens das zurück (Auszug) :

Code: Alles auswählen

<Assen> ........................................................ <Am 06.Aug.2008.>\\ntraurig\\n\\n<schmockefuckanalpropaganda(K\\xc3\\xb6nig)> ab in stadtgarten alle man(auf den hund gekommen) <Am 04.Aug.2008.>\\nquietschfidel\\n\\n<Ace L. Chuck> Dirty old Town... <Am 27.Jul.2008.>\\nerleichtert\\n\\n<The kid you never wanted around.> wohnt jezz auch wieder in schalke-ost! <Am 25.Jul.2008.>\\nersch\\xc3\\xb6pft\\n\\n<Dino> : wuff! <Am 15.Jul.2008.>\\nam Chillen\\n\\n<Philo> isst gerade. <Am 13.Jul.2008.>\\nam Rocken\\n\\n<Dirrrty Sanchez from Outta Space> quo <Am 22.Jun.2008.>\\ntriumphierend\\n\\n<Horst> endlich neue wohnung UND internet!!! <Am 10.Jun.2008.>\\naufgedreht\\n\\n<Elektro_Deluxe> : In Deinem erweiterten Netzwerk <Am 08.Mai.2008.>\\nritterlich\\n\\n
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Leonidas hat geschrieben:Und wozu das ganze? html5lib und lxml.html existieren.
Du meinst, ich soll mit lxml parsen (mit html5lib tu ich's ja schon und baue daraus einen lxml-Baum)? Das Problem ist, dass lxml wohl nicht so fehlertolerant ist. Zumindest bekomme bei dessen Parser nichts brauchbares zurück. :(
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

snafu hat geschrieben:Du meinst, ich soll mit lxml parsen (mit html5lib tu ich's ja schon und baue daraus einen lxml-Baum)? Das Problem ist, dass lxml wohl nicht so fehlertolerant ist. Zumindest bekomme bei dessen Parser nichts brauchbares zurück. :(
Und wie sieht das mit html5lib aus?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
lunar

snafu hat geschrieben:
Leonidas hat geschrieben:Und wozu das ganze? html5lib und lxml.html existieren.
Du meinst, ich soll mit lxml parsen (mit html5lib tu ich's ja schon und baue daraus einen lxml-Baum)? Das Problem ist, dass lxml wohl nicht so fehlertolerant ist. Zumindest bekomme bei dessen Parser nichts brauchbares zurück. :(
Ist myspace wirklich so kaputt oder hast du nur einfach nicht mit lxml.html geparst (sondern mit lxml.etree)?
BlackJack

@Hyperion: Du hast da im Endeffekt eine Unicode-Zeichenkette stehen, die Du eben *nicht* explizit kodierst, sondern das Python überlässt. Und wenn da was ausserhalb von ASCII enthalten ist, dann kann's krachen.

Nochmal langsam ``u'%s'.encode('iso-8859-1') % get_moods().decode("iso-8859-1")`` Schritt für Schritt. Der Ausdruck vor dem ``%`` wird zu '%s', das hatten wir ja schon. Gehen wir mal davon aus `get_moods()` gibt 'mööp' als 'iso-8859-1' kodiert zurück, dann wird daraus u'm\xf6\xf6p'. Im nächsten Schritt wird also ``'%s' % u'm\xf6\xf6p'`` ausgwertet. Bei solchen Operationen mit Zeichenketten und Unicode-Objekten wird immer die Zeichenkette implizit zu Unicode umgewandelt (mit ASCII als angenommene Kodierung, aber das ist hier ja kein Problem). Das ist so ähnlich wie Operationen mit ganzen Zahlen und Fliesskommazahlen, wo immer mit Fliesskommazahlen weiter gearbeitet wird. Also läuft's auf ``u'%s' % u'm\xf6\xf6p'`` raus, was auch wieder so eine unnütze Aktion ist, weil's natürlich wieder u'm\xf6\xf6p' als Ergebnis hat. Und da steht jetzt ein ``print`` davor. Damit liefert man sich wieder dem Problem aus, dass das klappen kann, oder auch nicht, je nachdem ob Python die Kodierung von `sys.stdout` richtig raten konnte oder nicht.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@BlackJack: Danke für die ausführliche Erklärung :-) Dass bei Operationen mit Zeichenketten und Unicode die Zeichenkette immer intern in Unicode gewandelt wird, war mir wirklich nicht bewusst.

Wie geht man denn nun aber in diesem Falle vor? Also kann ich print irgend wie klar machen, welche Kodierung ein String hat oder wie es etwas korrekt ausgeben soll? In obigen Fall will es ja offensichtlich den ByteString als ASCII ausgeben, was ja das Problem darstellt.
BlackJack

Naja, entweder man kodiert das Unicode-Objekt direkt beim ``print`` in Bytes, oder man "wrappt" die Ausgabe `sys.stdout` mit einem entsprechenden Objekt aus dem `encodings`-Modul und benutzt dort dann die `write()`-Methode.

Welche Kodierung das Programm, das an `sys.stdout` auf Bytes lauscht, erwartet, kann man nur raten und man sollte IMHO immer die Möglichkeit bieten den Anwender des Programms entscheiden zu lassen.
Antworten