Seite 1 von 1

imaplib: Problem mit Zeichencodierung

Verfasst: Mittwoch 20. Februar 2008, 11:20
von burli
Hi, ich versuche mit Hilfe der imaplib Emails zu bearbeiten. Das funktioniert soweit. Nur die Zeichencodierung passt nicht. Die Mails liegen im ISO 8859 Standard vor, aber Python verarbeitet das anscheinend als ASCII (also nur 0-127).

Jetzt weiß ich nicht ob man imaplib.fetch() dazu überreden kann das als ISO 8859-15 zu behandeln oder gleich in Unicode zu konvertieren. Geht das irgendwie?

Code: Alles auswählen

import imaplib

im=imaplib.IMAP4('imap.server.de')

im.login('user', 'passwd')
im.select("INBOX")

ok, result = im.search(None, 'UNSEEN')

for mail in result[0].split():
    print mail
    typ, daten = im.fetch(mail,  "(BODY[TEXT])")
    print "%s\n+++\n" % daten[0][1]
im.close()
im.logout()

Verfasst: Mittwoch 20. Februar 2008, 11:55
von BlackJack
Mails sind normalerweise nur ASCII. Alles ausserhalb von 0-127 wird in der Regel irgendwie kodiert. Wie sehen denn zum Beispiel Umlaute bei Dir aus?

Verfasst: Mittwoch 20. Februar 2008, 11:58
von burli
diese invertierten ? in einer Raute

Verfasst: Mittwoch 20. Februar 2008, 12:36
von BlackJack
Äh, okay wie sehen die Daten *wirklich* aus. Bis Du zu den � kommst, werden die Daten ja noch mindestens einmal durch irgendein Programm interpretiert, dass die Bytes als Zeichen anzeigt.

Also was ergibt: ``print type(s)`` und ``print repr(s)`` für die Daten?

Verfasst: Mittwoch 20. Februar 2008, 12:49
von burli
Also, das komplette Programm sieht du oben. Die relevanten Daten stehen in "daten". daten ist eine List.

daten[0] ist ein Tuple, daten[1] ist ein String. Ich hab jetzt mal folgendes gemacht

Code: Alles auswählen

for mail in result[0].split():
    typ, daten = im.fetch(mail,  "(BODY[TEXT])")
    print type(daten[0])
    print "----"
    print daten[0]
    print "----"
    print type(daten[1])
    print "----"
    print daten[1]
    print "----"
    print daten[0][1]
Als ergebnis bekomme ich
<type 'tuple'>
----
('107 (FLAGS (\\Seen) BODY[TEXT] {18}', 'Test Umlaut \xe4\xf6\xfc\xdf\r\n')
----
<type 'str'>
----
)
----
Test Umlaut ���
EDIT: Ok, ich bin gerade auf etwas reingefallen. mit daten[0][1] greift man auf den String in dem Tuple zu, also

Test Umlaut \xe4\xf6\xfc\xdf\r\n

Die Umlaute sind, wie du schon vermutet hast, verschlüsselt. Die muss ich jetzt nur noch entschlüsseln. Aber wie?

Verfasst: Mittwoch 20. Februar 2008, 13:14
von BlackJack
Das sind auf jeden Fall schon mal Bytes ausserhalb von ASCII. Also die dort scheinen als iso8859-1 kodiert zu sein.

Code: Alles auswählen

In [467]: '\xe4\xf6\xfc\xdf'.decode('iso8859-15')
Out[467]: u'\xe4\xf6\xfc\xdf'

In [468]: print '\xe4\xf6\xfc\xdf'.decode('iso8859-15')
äöüß

Verfasst: Mittwoch 20. Februar 2008, 13:28
von burli
Sowas ähnliches hab ich auch schon probiert.

Code: Alles auswählen

    print daten[0][1].decode('iso8859-15')
Dabei kommt aber folgende Fehlermeldung
Traceback (most recent call last):
File "/workspace/imap/src/imap/__init__.py", line 17, in <module>
print daten[0][1].decode('iso8859-15')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 12-15: ordinal not in range(128)
Bei Deiner Zeile kommt im übrigen die gleiche Meldung wenn ich die in das Programm einfüge. Wenn ich die dagegen in einer Python Shell wie IDLE eingebe funktioniert es. Hab nur keine Ahnung warum

Verfasst: Mittwoch 20. Februar 2008, 13:37
von BlackJack
Die Ausnahme kommt nicht vom `decode()`, wie man schön sehen kann ist das ein `UnicodeEncodeError`! Das ``print`` muss ja nach aussen wieder Bytes rausgeben und versucht deshalb das Unicode-Objekt wieder in solche um zu wandeln. Da Python an der Stelle nicht weiss, welche Kodierung die Anwendung auf `stdout` erwartet, wird's mit ASCII versucht. Das geht natürlich mit Umlauten nicht.

Setz Dich mal mit Unicode auseinander! Unicode kann nur innerhalb eines Programms existieren. Daten von "draussen" und nach "draussen" sind immer irgendwie als Bytes kodiert. Und das musst *Du* machen, weil man den Daten nicht ansehen kann, *wie* sie kodiert sind. Das muss der Programmierer wissen.

Verfasst: Mittwoch 20. Februar 2008, 13:44
von burli
Ah, ok. Dh wenn ich im Programm zb

Code: Alles auswählen

test=daten[0][1].decode('iso8859-15') 
schreibe ist test ein Unicode String in dem die Umlaute von ISO8859 in Unicode umgewandelt wurden. print kann den String aber trotz allem nicht ausgeben weil print nicht weiß wie die Sonderzeichen ausgegeben werden sollen. In der Shell funktioniert es wohl deshalb weil die Shell selbst komplett als Unicode läuft?

Verfasst: Mittwoch 20. Februar 2008, 13:58
von BlackJack
Das funktioniert immer dann, wenn `sys.stdout.encoding` nicht `None` ist und stimmt. Das ist in IDLE der Fall und unter Linux in den üblichen Terminals auch.

Verfasst: Mittwoch 20. Februar 2008, 14:46
von burli
Ok, ich hab zwar ein paar Sachen gefunden, aber wie ich sys.stdout.encoding umstelle noch nicht.

Code: Alles auswählen

print sys.getfilesystemencoding()
print sys.getdefaultencoding()
print sys.stdout.encoding
ergibt
UTF-8
ascii
None
Zumindest temporär funktioniert es jetzt

Code: Alles auswählen

test = '\xe4\xf6\xfc\xdf'.decode('iso8859-15')
print test.encode('utf-8')
Aber vielleicht kannst du mir noch verraten wie ich sys.stdout.encoding setzen kann? Dazu hab ich noch nichts finden können. Versuche mit sys.setdefaultencoding='utf-8' oder ähnlichem haben noch nicht gefruchtet

Verfasst: Mittwoch 20. Februar 2008, 15:00
von BlackJack
Das kann man gar nicht setzen. Du kannst die Sachen im `codecs`-Modul benutzen, um die Standardausgabe in ein entsprechendes Objekt zu verpacken, damit transparent kodiert wird.

Verfasst: Mittwoch 20. Februar 2008, 15:07
von burli
Mhm, ok, das krieg ich jetzt beim schnellen Überfliegen nicht auf die Reihe. Das muss ich in einer ruhigen Minute mal durcharbeiten. Aber zumindest kann ich jetzt weitermachen.

Ich danke dir recht herzlich für die Hilfe