imaplib: Problem mit Zeichencodierung

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

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()
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?
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

diese invertierten ? in einer Raute
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?
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

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?
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')
äöüß
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

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
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.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

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?
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.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

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
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.
burli
User
Beiträge: 1156
Registriert: Dienstag 9. März 2004, 18:22

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
Antworten