Seite 1 von 2

Wie Umlaute statt "komische Zeichen" anzeigen?

Verfasst: Mittwoch 17. September 2008, 18:10
von snafu
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

Verfasst: Mittwoch 17. September 2008, 18:15
von sea-live
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

Verfasst: Mittwoch 17. September 2008, 18:20
von Hyperion
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 ;-)

Verfasst: Mittwoch 17. September 2008, 18:43
von snafu
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)

Verfasst: Mittwoch 17. September 2008, 18:54
von Hyperion
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")

Verfasst: Mittwoch 17. September 2008, 19:09
von 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.

Verfasst: Mittwoch 17. September 2008, 19:11
von Leonidas
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.

Verfasst: Mittwoch 17. September 2008, 19:14
von snafu
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...

Verfasst: Mittwoch 17. September 2008, 19:34
von 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.

Verfasst: Mittwoch 17. September 2008, 19:35
von snafu
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?

Verfasst: Mittwoch 17. September 2008, 19:54
von Leonidas
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.

Verfasst: Mittwoch 17. September 2008, 20:10
von Hyperion
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?

Verfasst: Mittwoch 17. September 2008, 20:13
von snafu
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.

Verfasst: Mittwoch 17. September 2008, 20:21
von snafu
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

Verfasst: Mittwoch 17. September 2008, 20:59
von snafu
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. :(

Verfasst: Mittwoch 17. September 2008, 21:17
von Leonidas
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?

Verfasst: Mittwoch 17. September 2008, 21:47
von 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)?

Verfasst: Mittwoch 17. September 2008, 22:03
von 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.

Verfasst: Donnerstag 18. September 2008, 07:45
von Hyperion
@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.

Verfasst: Donnerstag 18. September 2008, 08:14
von 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.