34.4 E‐Mail
In diesem Abschnitt werden wir Module der Standardbibliothek vorstellen, die es ermöglichen, mit einem E‐Mail-Server zu kommunizieren, das heißt, E‐Mails von diesem abzuholen bzw. E‐Mails über den Server zu versenden.
Das Versenden einer E‐Mail erfolgt über einen sogenannten SMTP-Server, mit dem über ein gleichnamiges Protokoll kommuniziert werden kann. Im ersten Unterabschnitt werden wir Ihnen deshalb das Modul smtplib der Standardbibliothek vorstellen, das dieses Kommunikationsprotokoll implementiert.
Für das Herunterladen einer empfangenen E‐Mail gibt es zwei verbreitete Möglichkeiten: das POP3- und das IMAP4-Protokoll. Beide können mit dem jeweiligen Modul poplib bzw. imaplib verwendet werden.
Im letzten Abschnitt wird das Modul email der Standardbibliothek besprochen, das es über die MIME-Codierung ermöglicht, beliebige Dateien (üblicherweise Bilder oder Dokumente) mit der E‐Mail zu versenden.
34.4.1 SMTP – smtplib
Das SMTP-Protokoll (für Simple Mail Transfer Protocol) wird zum Versenden einer E‐Mail über einen SMTP-Server verwendet. Das SMTP-Protokoll ist ähnlich wie FTP ein textbasiertes, menschenlesbares Protokoll. Ursprünglich bot das SMTP-Protokoll keine Möglichkeit zur Authentifizierung des angemeldeten Benutzers, beispielsweise durch Benutzernamen und Passwort. Dies war bei der rasanten Entwicklung des Internets schnell nicht mehr tragbar, und so wurde das SMTP-Protokoll um den ESMTP-Standard (Extended SMTP) erweitert. Tabelle 34.9 listet die wichtigsten SMTP-Befehle in der Reihenfolge ihrer Benutzung in einer SMTP-Sitzung auf und erklärt sie kurz.
Befehl | Beschreibung |
---|---|
HELO | Startet eine SMTP-Sitzung. |
EHLO | Startet eine ESMTP-Sitzung. |
MAIL FROM | Leitet das Absenden einer E‐Mail ein. Diesem Kommando wird die Absenderadresse beigefügt. |
RCPT TO | Fügt einen Empfänger der E‐Mail hinzu. (RCPT steht für Recipient, dt. »Empfänger«.) |
DATA | Mit diesem Kommando wird der Inhalt der E‐Mail angegeben und die Mail schließlich verschickt. |
QUIT | Beendet die SMTP- bzw. ESMTP-Sitzung. |
Das Modul smtplib enthält im Wesentlichen eine Klasse namens SMTP. Nachdem diese Klasse instanziiert wurde, läuft über sie alle weitere Kommunikation mit dem Server.
SMTP([host, port, local_hostname, timeout, source_address])
Beim Erzeugen der Klasse SMTP können optional bereits die Verbindungsdaten zum SMTP-Server übergeben werden. Der Port muss nur explizit angegeben werden, wenn er sich vom SMTP-Standardport 25 unterscheidet.
Als dritter Parameter kann der Domainname des lokalen Hosts übergeben werden. Dieser wird dem SMTP-Server als Identifikation im ersten gesendeten Kommando übermittelt. Wenn der Parameter local_hostname nicht angegeben wird, wird versucht, den lokalen Hostnamen automatisch zu ermitteln.
Für den vierten Parameter können Sie einen speziellen Timeout-Wert in Sekunden übergeben, der bei der Verbindung zum SMTP-Server berücksichtigt wird. Wenn Sie timeout nicht angeben, wird ein Standardwert verwendet.
Mit source_address wird die Herkunftsadresse, die bei der Verbindung verwendet werden soll, als Tupel im Format (ip, port) angegeben. Um die Beispiele der folgenden Abschnitte nachvollziehen zu können, muss das Modul smtplib eingebunden werden und eine Instanz der Klasse SMTP mit dem Namen s existieren:
>>> import smtplib
>>> s = smtplib.SMTP("smtp.server.de")
[»] Hinweis
Die SMTP-Klasse lässt sich mit dem with-Statement verwenden:
with smtplib.SMTP("smtp.server.de") as s:
s.login("Benutzername", "Passwort")
Eine Verbindung aufbauen und beenden
Das Aufbauen einer SMTP-Verbindung erfolgt in der Regel in zwei Schritten: dem eigentlichen Verbindungsaufbau und dem Login. Im Modul smtplib werden diese beiden Schritte durch die Funktionen connect und login repräsentiert:
>>> s.connect("smtp.server.de", 25)
(220, 'Die Botschaft des Servers')
>>> s.login("Benutzername", "Passwort")
(235, '2.0.0 Authentication successful')
Die Angabe des Ports bei connect ist optional und kann weggelassen werden, wenn es sich um den SMTP-Standardport 25 handelt.
Zum Beenden einer SMTP-Verbindung wird die Funktion quit aufgerufen.
Eine E‐Mail versenden
Zum Versenden einer E‐Mail über SMTP existiert die Funktion sendmail mit der folgenden Schnittstelle:
sendmail(from_addr, to_addrs, msg, [mail_options, rctp_options])
Beachten Sie, dass die SMTP-Instanz dafür an einem SMTP-Server angemeldet und zumeist auch authentifiziert sein muss.
Die ersten beiden Parameter enthalten die E‐Mail-Adressen des Absenders (from_addr) bzw. eine Liste der E‐Mail-Adressen der Empfänger (to_addr). Als E‐Mail-Adresse wird dabei ein String des folgenden Formats bezeichnet:
Vorname Nachname <em@il.addr>
Alternativ kann auch nur die E‐Mail-Adresse im String stehen.
Als dritten Parameter, msg, übergeben Sie den Text der E‐Mail. Hier werden auch weitere Angaben wie beispielsweise der Betreff der E‐Mail definiert. Wie so etwas genau aussieht und welche Möglichkeiten Python bietet, diesen Header komfortabel zu erzeugen, erfahren Sie in Abschnitt 34.4.4, »Erstellen komplexer E‐Mails – email«.
Die Methode sendmail gibt stets ein Dictionary zurück, in dem alle Empfänger, die vom SMTP-Server zurückgewiesen wurden, als Schlüssel enthalten sind und der jeweilige Error-Code mit Fehlerbezeichnung als Wert aufgeführt ist. Wenn alle Empfänger die E‐Mail bekommen haben, ist das zurückgegebene Dictionary leer.
[»] Hinweis
Der Text einer E‐Mail darf nur aus ASCII-Zeichen bestehen. Um auch andere Zeichen und insbesondere auch Binärdaten verschicken zu können, bedient man sich der sogenannten MIME-Codierung, die wir in Abschnitt 34.4.4, »Erstellen komplexer E‐Mails – email«, behandeln werden.
Über die optionalen Parameter mail_options und rcpt_options kann je eine Liste von Strings übergeben werden, die Optionen des ESMTP-Standards (Extended SMTP) enthalten. Die für mail_options übergebenen Optionen werden dem Kommando MAIL FROM angefügt, während die für rcpt_options übergebenen Optionen dem Kommando RCPT TO angehängt werden.
Beispiel
Nachdem Sie die wichtigsten Methoden einer SMTP-Instanz kennengelernt haben, folgt nun ein kleines Beispiel, in dem zu einem SMTP-Server verbunden wird, um zwei E‐Mails an verschiedene Empfänger zu verschicken:
>>> smtp = smtplib.SMTP("smtp.server.de", 25)
>>> smtp.login("Benutzername", "Passwort")
(235, '2.0.0 Authentication successful')
>>> smtp.sendmail(
... "Peter Kaiser <kaiser@python-buch.de>",
... "Johannes Ernesti <ernesti@python-buch.de>",
... "Dies ist der Text")
{}
>>> smtp.sendmail(
... "Peter Kaiser <kaiser@python-buch.de>",
... ["ernesti@python-buch.de", "kaiser@python-buch.de"],
... "Dies ist der Text")
{}
>>> smtp.quit()
Bei der ersten E‐Mail wurden die vollen Namen des Absenders bzw. des Empfängers angegeben. Das zweite Beispiel zeigt, dass auch die E‐Mail-Adresse allein reicht, und demonstriert, wie eine E‐Mail an mehrere Empfänger versandt werden kann.
34.4.2 POP3 – poplib
Nachdem anhand von smtplib erläutert wurde, wie E‐Mails über einen SMTP-Server versandt werden können, besprechen wir in diesem Abschnitt das Modul poplib der Standardbibliothek. Dieses Modul implementiert das POP3-Protokoll (Post Office Protocol Version 3). Bei POP3 handelt es sich um ein Protokoll, das dazu verwendet wird, auf einem POP3-Server gespeicherte E‐Mails einzusehen und abzuholen. Das POP3-Protokoll steht damit in Konkurrenz zu IMAP4, dessen Benutzung mit der imaplib im nächsten Abschnitt besprochen wird.
Tabelle 34.10 listet die wichtigsten POP3-Kommandos mit ihrer Bedeutung auf. Die Befehle stehen dabei in der Reihenfolge, wie sie in einer üblichen POP3-Sitzung verwendet werden.
Befehl | Beschreibung |
---|---|
USER | Überträgt den Benutzernamen zur Authentifizierung auf dem Server. |
PASS | Überträgt das Passwort zur Authentifizierung auf dem Server. |
STAT | Liefert den Status des Posteingangs, beispielsweise die Anzahl der neu eingegangenen E‐Mails. |
LIST | Liefert Informationen zu einer bestimmten E‐Mail des Posteingangs. |
RETR | Überträgt eine bestimmte E‐Mail. |
DELE | Löscht eine bestimmte E‐Mail. |
RSET | Widerruft alle anstehenden Löschvorgänge.* |
QUIT | Beendet die POP3-Sitzung. |
* Löschvorgänge werden gepuffert und erst am Ende der Sitzung ausgeführt. |
Wie bereits beim Modul smtplib ist im Modul poplib im Wesentlichen die Klasse POP3 enthalten, die instanziiert werden muss, bevor Operationen auf einem POP3-Server durchgeführt werden können. Die Schnittstelle des Konstruktors wird im folgenden Abschnitt beschrieben.
POP3(host, [port, timeout])
Dem Konstruktor der Klasse POP3 wird der Hostname des Servers übergeben, zu dem verbunden werden soll. Optional kann ein Port angegeben werden, wenn dieser sich vom voreingestellten Standardport 110 unterscheiden soll. Zusätzlich kann ein Timeout in Sekunden angegeben werden, der bei der Verbindung zum Server berücksichtigt wird.
Um die in den folgenden Abschnitten vorgestellten Beispiele ausführen zu können, muss zum einen das Modul poplib eingebunden sein und zum anderen eine Instanz der Klasse POP3 mit dem Namen pop existieren:
>>> import poplib
>>> pop = poplib.POP3("pop.server.de")
Für die meisten Beispiele muss diese Instanz außerdem mit einem POP3-Server verbunden und bei diesem authentifiziert sein.
Eine Verbindung aufbauen und beenden
Nach der Instanziierung der Klasse POP3, bei der bereits zum Zielserver verbunden wird, folgt in der Regel ein Authentifizierungsschritt. Dazu müssen die Methoden user und pass_ der POP3-Instanz aufgerufen werden:
>>> pop.user("Benutzername")
b'+OK'
>>> pop.pass_("Passwort")
b'+OK logged in.'
[»] Hinweis
Der bevorzugte Name pass für die Passwort-Methode ist in Python bereits mit einem Schlüsselwort belegt. In solchen Fällen wird an den belegten Namen häufig ein Unterstrich angehängt.
Nachdem das Passwort vom Server akzeptiert worden ist, darf auf den Posteingang zugegriffen werden. Dieser ist bis zum Aufruf von quit für andere Login-Versuche gesperrt:
>>> pop.quit()
b'+OK Logging out'
Vorhandene E‐Mails auflisten
Nach der Authentifizierung bei einem POP3-Server können die dort gespeicherten E‐Mails eingesehen und gelöscht werden. Dazu können Sie sich zunächst mithilfe der Methoden stat und list einen Überblick über die vorhandenen E‐Mails verschaffen.
Die Methode stat gibt den Status des Posteingangs zurück. Das Ergebnis ist ein Tupel mit zwei ganzen Zahlen: der Anzahl der enthaltenen Nachrichten und der Größe des Posteingangs in Byte.
>>> pop.stat()
(1, 623)
In diesem Fall befindet sich eine E‐Mail im Posteingang, und die Gesamtgröße des Posteingangs beläuft sich auf 623 Byte.
Die Methode list gibt eine Liste der im Posteingang liegenden Mails zurück. Der Rückgabewert dieser Methode ist ein Tupel der folgenden Form:
(antwort, [b"mailID länge", ...], datlen)
Dabei enthält das Tupel als erstes Element den Antwort-String des Servers und als zweites Element eine Liste von bytes-Strings, die je für eine E‐Mail des Posteingangs stehen. Der String enthält zwei Angaben: Die Angabe mailID ist die laufende Nummer der Mail, eine Art Index, und laenge ist die Gesamtgröße der Mail in Byte. In Bezug auf den Index sollten Sie beachten, dass alle E‐Mails auf dem Server fortlaufend von 1 an indiziert werden.
>>> pop.list()
(b'+OK […].', [b'1 623'], 7)
Das erste Element des Tupels (antwort) enthält nicht den vollständigen Antwort-String des Servers, denn die Informationen, die zum zweiten Element des Tupels aufbereitet wurden, wurden aus antwort entfernt. Um dennoch die komplette Länge der Serverantwort berechnen zu können, existiert das dritte Element des Tupels (datlen). Dieses referenziert die Länge des Datenbereichs der Antwort des Servers. Damit entspräche len(antwort) + datlen der Gesamtgröße des vom Server tatsächlich gesendeten Antwort-Strings.
Optional kann die laufende Nummer einer E‐Mail angegeben werden, über die nähere Informationen zurückgegeben werden sollen. In diesem Fall gibt die Methode einen bytes-String des Formats b"+OK mailID länge" zurück:
>>> pop.list(1)
b'+OK 1 623'
E‐Mails abrufen und löschen
Mithilfe der Methode retr lässt sich eine E‐Mail abrufen. Diese Methode gibt den Inhalt der E‐Mail in Form des folgenden Tupels zurück:
(antwort, zeilen, länge)
Das erste Element des Tupels entspricht dem Antwort-String des Servers.[ 150 ](Im Antwort-String ist von »623 octets« die Rede. Mit Octets (dt. »Achtergruppen«) sind Bytes gemeint. ) An zweiter Stelle steht eine Liste von bytes-Strings, die je eine Zeile der E‐Mail inklusive des E‐Mail-Headers enthalten. Das letzte Element des Tupels ist die Größe der E‐Mail in Byte.
Im folgenden Beispiel wird die E‐Mail mit der laufenden Nummer 1 vom Server abgerufen:
>>> pop.retr(1)
(b'+OK 623 octets follow.', […], 623)
Anstelle des Auslassungszeichens stünde eine Liste von Strings, die die Zeilen der vollständigen E‐Mail enthält.
In analoger Art und Weise funktioniert das Löschen einer E‐Mail über die Methode dele:
>>> pop.dele(1)
b'+OK Deleted.'
[»] Hinweis
Beachten Sie, dass die meisten Server Löschbefehle puffern und erst nach Aufruf der Methode quit tatsächlich ausführen.
In einem solchen Szenario kann die Methode rset aufgerufen werden, um alle anstehenden Löschvorgänge zu verwerfen.
Beispiel
Das folgende Beispielprogramm verwendet das Modul poplib dazu, alle E‐Mails von einem POP3-Server abzuholen und auf dem Bildschirm anzuzeigen:
import poplib
pop = poplib.POP3("pop.server.de")
pop.user("benutzername")
pop.pass_("passwort")
for i in range(1, pop.stat()[0]+1):
for zeile in pop.retr(i)[1]:
print(zeile)
print("***")
pop.quit()
Zunächst wird eine Instanz der Klasse POP3 erzeugt, und das Programm meldet sich mit den Methoden user und pass_ beim POP3-Server an. Der Ausdruck pop.stat()[0] liefert die Zahl der Mails, die sich im Posteingang befinden. In der for-Schleife werden also alle Mail-Indizes durchlaufen. Die Indizierung der E‐Mails im Posteingang beginnt mit 1.
In der inneren Schleife wird die jeweils aktuelle Mail mit dem Index i durch Aufruf der Methode retr heruntergeladen. Das zweite Element, also das mit dem Index 1 des von dieser Methode zurückgegebenen Tupels, enthält eine Liste mit allen Zeilen des Mail-Inhalts. Diese Liste wird in der Schleife durchlaufen, und es wird jeweils die aktuelle Zeile ausgegeben.
[»] Hinweis
Aus Gründen der Übersichtlichkeit wurde im Beispielprogramm auf jegliche Fehlerbehandlung verzichtet. In einem fertigen Programm sollten Sie prüfen, ob die Verbindung zum Server hergestellt werden konnte und ob die Authentifizierung erfolgreich war.
34.4.3 IMAP4 – imaplib
Das Modul imaplib stellt die Klasse IMAP4 zur Verfügung, mit deren Hilfe Sie eine Verbindung zu einem IMAP4-Server herstellen und mit diesem kommunizieren. Das IMAP4-Protokoll (Internet Message Access Protocol 4) ist ähnlich wie das POP3-Protokoll zur Verwaltung von E‐Mails auf einem Mail-Server gedacht. Anders als bei POP3 verbleiben die E‐Mails bei IMAP4 zumeist auf dem Mail-Server, was den Vorteil hat, dass man von überall – beispielsweise auch von einem Internet-Café im Urlaub aus – vollen Zugriff auf alle archivierten E‐Mails hat. Heutzutage bieten die meisten E‐Mail-Anbieter sowohl einen POP3- als auch einen IMAP4-Zugang an. Im Vergleich zu POP3 unterstützt IMAP4 Kommandos zur komfortablen Verwaltung der Mails auf dem Server. So können beispielsweise Unterordner angelegt werden.
Im Gegensatz zu den bisherigen Protokollen wie FTP oder POP3 ist IMAP4 mit einem hohen Funktionsumfang ausgestattet, und obwohl das Protokoll immer noch auf lesbaren Textnachrichten basiert, ist es zu komplex, um es im Stil der bisherigen Abschnitte mit einer kurzen Tabelle ausreichend zu beschreiben.
Bei IMAP4 lassen sich E‐Mails in verschiedene Mailboxen einsortieren. Dabei können Sie sich eine Mailbox als ein Verzeichnis vorstellen, das E‐Mails enthalten kann, wie ein Ordner Dateien enthält. Die Mailbox-Struktur des verwendeten Beispielservers sieht folgendermaßen aus:
Es existieren eine übergeordnete Mailbox namens INBOX sowie zwei untergeordnete Mailboxen namens INBOX.Ham und INBOX.Spam.
IMAP4([host, port])
Um eine Verbindung zu einem IMAP4-Server herzustellen, muss eine Instanz der Klasse IMAP4 erzeugt werden. Der Konstruktor dieser Klasse kann direkt eine Verbindung zu einem IMAP4-Server mit dem Hostnamen host unter Verwendung des Ports port aufbauen. Wenn der Parameter port nicht angegeben wurde, wird der IMAP4-Standardport 143 verwendet.
Nachdem eine Instanz der Klasse IMAP4 erzeugt wurde, stellt diese verschiedene Methoden bereit, um mit dem verbundenen Server zu kommunizieren. Jede Methode, die ein IMAP4-Kommando repräsentiert, gibt ein Tupel der folgenden Form zurück:
(Status, [Daten, …])
Dabei steht im resultierenden Tupel für Status entweder "OK" oder "NO", je nachdem, ob die Operation erfolgreich verlaufen oder fehlgeschlagen ist. Das zweite Element des Tupels ist eine Liste der Daten, die der Server als Antwort geschickt hat. Diese Daten können entweder bytes-Strings oder Tupel sein. Wenn es sich um Tupel handelt, verfügen diese über jeweils zwei Elemente, beides bytes-Strings:
(Header, Daten)
Die Beispiele in den folgenden Abschnitten setzen zumeist eine verbundene IMAP4-Instanz im voraus:
>>> import imaplib
>>> im = imaplib.IMAP4("imap.server.de")
In den meisten Fällen muss die IMAP4-Instanz zudem beim Server eingeloggt sein, was im folgenden Abschnitt behandelt wird.
[»] Hinweis
Die IMAP4-Klasse ist seit Python 3.5 ein Kontextmanager und lässt sich mit der with-Anweisung verwenden:
with imaplib.IMAP4("imap.server.de") as i:
pass
Eine Verbindung aufbauen und beenden
Sofern die IMAP4-Instanz im nicht bereits bei ihrer Erzeugung mit einem Server verbunden wurde, lässt sich dies über die Methode open nachholen. Diese verfügt ebenfalls über die Parameter host und port, analog zur Schnittstelle des IMAP4-Konstruktors.
Im zweiten Schritt wird die Methode login gerufen, um sich beim Server zu authentifizieren. Diese Methode bekommt den Benutzernamen und das Passwort als String übergeben. Eine bestehende Verbindung können Sie über die Methode logout beenden.
>>> im.login("Benutzername", "Passwort")
('OK', [b'Logged in'])
>>> im.logout()
('BYE', [b'Logging out'])
Eine Mailbox suchen und auswählen
Nach der Verbindung zu einem IMAP4-Server muss eine Mailbox ausgewählt werden, um an die darin enthaltenen E‐Mails zu gelangen. Die Methode list gibt die Namen aller Mailboxen zurück, die auf ein bestimmtes Pattern passen. Dazu bekommt sie den Basisordner übergeben, in dem gesucht werden soll, sowie das Pattern, auf das die Ergebnisse passen müssen. Das übergebene Pattern muss ein String sein und enthält üblicherweise Fragmente eines Mailbox-Namens inklusive Platzhalter *:
>>> im.list(".", "*Ham")
('OK', [b'(\\HasNoChildren) "." "INBOX.Ham"'])
>>> im.list(".", "*am")
('OK', [b'(\\HasNoChildren) "." "INBOX.Ham"',
b'(\\HasNoChildren) "." "INBOX.Spam"'])
>>> im.list(".", "*")
('OK', [b'(\\HasNoChildren) "." "INBOX.Ham"',
b'(\\HasNoChildren) "." "INBOX.Spam"',
b'(\\Unmarked \\HasChildren) "." "INBOX"'])
>>> im.list(".", "NichtVorhandeneMailbox")
('OK', [None])
Wenn kein Verzeichnis übergeben wird, werden Mailboxen des Hauptordners zurückgegeben. Geben Sie kein Pattern an, werden alle im jeweiligen Ordner enthaltenen Mailboxen zurückgegeben.
Jeder Eintrag der Ergebnisliste ist ein bytes-String und enthält drei jeweils durch ein Leerzeichen voneinander getrennte Informationen: die sogenannten Flags der Mailbox in Klammern, das Verzeichnis der Mailbox und den Mailbox-Namen, jeweils in doppelten Anführungsstrichen. Aus den Flags kann man beispielsweise die Information entnehmen, ob eine Mailbox untergeordnete Mailboxen enthält (\HasChildren) oder nicht (\HasNoChildren).
Nachdem eine passende Mailbox gefunden wurde, kann sie durch Aufruf der Methode select ausgewählt werden, um weitere Operationen auf ihr durchführen zu können. Dabei übergeben Sie als ersten Parameter den Namen der auszuwählenden Mailbox. Die Methode select gibt die Anzahl der E‐Mails zurück, die sich in der gewählten Mailbox befinden.
>>> im.select("INBOX")
('OK', [b'2'])
Es wird keine Exception geworfen, wenn die gewünschte Mailbox nicht existiert. Stattdessen muss der Fehler anhand des Rückgabewertes ausgemacht werden:
>>> im.select("INBOX.NichtExistent")
('NO', [b'Mailbox does not exist, or must be subscribed to.'])
Eine ausgewählte Mailbox kann über die Methode close geschlossen werden.
Operationen mit Mailboxen
Neben dem Auswählen und Schließen lassen sich weitere Operationen mit Mailboxen durchführen, die in Tabelle 34.11 kurz zusammengefasst werden. Jede dieser Operationen kann über eine entsprechende Methode einer verbundenen IMAP4-Instanz ausgeführt werden und bekommt einen oder mehrere Mailbox-Namen übergeben.
Methode | Beschreibung |
---|---|
create(mailbox) | Erstellt eine neue Mailbox namens mailbox. |
delete(mailbox) | Löscht die Mailbox mailbox. |
rename(oldmailbox, newmailbox) | Benennt die Mailbox oldmailbox in newmailbox um. |
E‐Mails suchen
Die Methode search sucht innerhalb der ausgewählten Mailbox nach E‐Mails, die auf ein oder mehrere angegebene Kriterien passen. Dazu hat die Methode die folgende Schnittstelle:
search(charset, [*criteria])
Als Kriterium kann entweder der String "ALL" (alle Mails erfüllen dieses Kriterium) oder ein String des Formats "(FROM \"Johannes\")" verwendet werden. Das zweite Kriterium ist für alle Mails erfüllt, die von einem gewissen »Johannes« geschrieben wurden. Der Parameter charset spezifiziert das Encoding der Kriterium-Strings. Üblicherweise wird dieser Parameter nicht benötigt und None übergeben.
Die Methode search gibt die IDs der gefundenen E‐Mails in Form einer Liste zurück.
>>> im.search(None, '(FROM "Johannes")')
('OK', [b'1 2 3'])
>>> im.search(None, '(FROM "Johann")')
('OK', [b'1 2 3'])
>>> im.search(None, '(FROM "Johanninski")')
('OK', [b''])
E‐Mails abrufen
Zum Abrufen von E‐Mails existiert die Methode fetch mit der folgenden Schnittstelle:
fetch(message_set, message_parts)
Der Parameter message_set muss ein String sein, der die Mail-IDs der herunterzuladenden E‐Mails enthält. Dabei können diese entweder einzeln im String vorkommen ("1"), als Bereich ("1:4" für Mails Nr. 1 bis 4), als Liste von Bereichen ("1:4,7:9" für Mails Nr. 1 bis 4 und Nr. 7 bis 9) oder als Bereich mit unbestimmter oberer Grenze ("3:*" für alle Mails ab Mail Nr. 3).
Der zweite Parameter message_parts kennzeichnet, welche Teile der angegebenen E‐Mails heruntergeladen werden sollen. Ein Wert von "(RFC822)" bedeutet, die gesamte Mail, also inklusive des Mail-Headers, herunterzuladen. Bei einem Wert von "(BODY[TEXT])" wird nur der Text und bei "(BODY[HEADER])" nur der Header der E‐Mail heruntergeladen.
>>> im.fetch("1", "(BODY[TEXT])")
('OK', [(b'1 (BODY[TEXT] {29}',
b'Dies ist eine Testnachricht\r\n'), b')'])
>>> im.fetch("1:2", "(BODY[TEXT])")
('OK', [(b'1 (BODY[TEXT] {29}',
b'Dies ist eine Testnachricht\r\n'), b')',
(b'2 (BODY[TEXT] {25}',
b'Noch eine Testnachricht\r\n'), b')'])
Im Falle einer nicht vorhandenen Mail-ID wird keine Exception geworfen, sondern ein leeres Ergebnis zurückgegeben. Wenn die ID ungültig ist, kommt eine entsprechende Fehlermeldung zurück:
>>> im.fetch("100", "(BODY[TEXT])")
('OK', [None])
>>> im.fetch("KeineID", "(BODY[TEXT])")
('NO', [b'Error in IMAP command received by server.'])
Beispiel
Im folgenden Beispielprogramm wird das Modul imaplib dazu verwendet, zu einem IMAP4-Server zu verbinden und alle enthaltenen E‐Mails einer bestimmten Mailbox anzuzeigen. Dabei erhält der Benutzer die Möglichkeit, die Mailbox zu wählen. Der Quelltext des Beispielprogramms sieht folgendermaßen aus:
import imaplib
im = imaplib.IMAP4("imap.server.de")
im.login("Benutzername", "Passwort")
print("Vorhandene Mailboxen:")
for mb in im.list()[1]:
name = mb.split(b'"."')[-1]
print(" - {}".format(name.decode().strip(' "')))
mb = input("Welche Mailbox soll angezeigt werden: ")
im.select(mb)
status, daten = im.search(None, "ALL")
for mailnr in daten[0].split():
typ, daten = im.fetch(mailnr, "(RFC822)")
print("{}\n+++\n".format(daten[0][1].decode()))
im.close()
im.logout()
Zunächst wird eine Instanz der Klasse IMAP4 erzeugt und zu einem IMAP4-Server verbunden. Dann werden mithilfe der Methode list alle im Hauptordner des IMAP4-Kontos vorhandenen Mailboxen durchlaufen und die Namen der Mailboxen auf dem Bildschirm angezeigt. Beachten Sie dabei, dass die Methode list die Namen der Mailboxen mit zusätzlichen Informationen zurückgibt. Diese Informationen müssen herausgefiltert werden, bevor der Mailbox-Name angezeigt werden kann. Nachdem die Namen angezeigt wurden, wird der Benutzer dazu aufgefordert, einen der angegebenen Mailbox-Namen auszuwählen.
Die vom Benutzer ausgewählte Mailbox wird dann mithilfe der Methode select auch auf dem Server ausgewählt. Der danach aufgerufenen Methode search übergeben wir den String "ALL", was den Mail-Server dazu veranlasst, Daten über alle E‐Mails der ausgewählten Mailbox zurückzugeben.
Anschließend iterieren wir in einer for-Schleife über die Liste von Mail-IDs, die search zurückgegeben hat, und laden die jeweilige Mail mit fetch vollständig herunter. Die heruntergeladene Mail wird auf dem Bildschirm ausgegeben.
Zuletzt schließen wir die ausgewählte Mailbox und beenden die Verbindung mit dem Server.
Auch bei diesem Beispielprogramm wurde keine Fehlerbehandlung durchgeführt. In einem fertigen Programm sollten sowohl die Verbindungsanfrage als auch das Login und insbesondere die Benutzereingabe überprüft werden.
34.4.4 Erstellen komplexer E‐Mails – email
In den vorangegangenen Abschnitten haben Sie erfahren, wie Sie E‐Mails über einen SMTP-Server versenden und von einem POP3- oder IMAP4-Server herunterladen. Wie Sie wissen, basiert das Senden und Empfangen von E‐Mails auf reinen ASCII-Protokollen. Das bedeutet vor allem, dass mit diesen Protokollen keine Binärdaten verschickt werden können. Außerdem sind Sonderzeichen, die nicht dem 7-Bit-ASCII-Standard entsprechen, problematisch.
Um solche Zeichen oder Binärdaten verschicken zu können, wurde der MIME-Standard[ 151 ](Multipurpose Internet Mail Extension) entwickelt, der Sonderzeichen und Binärdaten so codiert, dass sie als eine Folge reiner ASCII-Zeichen versandt werden können. Zudem definiert der MIME-Standard verschiedene Dateitypen und legt eine Syntax fest, mit der Dateianhänge einem bestimmten Dateityp zugeordnet werden, sodass die Dateien beim Empfänger leichter verarbeitet werden können.
Das zugehörige Paket email ist sehr mächtig, weswegen hier nur ein Teil seines Funktionsumfangs besprochen werden kann. Zunächst werden wir uns darum kümmern, wie eine einfache ASCII-Mail mittels email erstellt werden kann. Darauf aufbauend werden wir zu komplexeren MIME-codierten Mails übergehen.
Erstellen einer einfachen E‐Mail
Als Basisklasse für eine neue E‐Mail dient die Klasse Message des Moduls email. message. Das folgende Beispielprogramm zeigt, wie sie verwendet wird:
from email.message import Message
msg = Message()
msg.set_payload("Dies ist meine selbst erstellte E‐Mail.")
msg["Subject"] = "Hallo Welt"
msg["From"] = "Donald Duck <don@ld.de>"
msg["To"] = "Onkel Dagobert <d@gobert.de>"
print(msg.as_string())
Die Ausgabe des Beispielprogramms, also die erzeugte E‐Mail, sieht folgendermaßen aus:
Subject: Hallo Welt
From: Donald Duck <don@ld.de>
To: Onkel Dagobert <d@gobert.de>
Dies ist meine selbst erstellte E‐Mail.
Zunächst erzeugen wir eine Instanz der Klasse Message. Der Konstruktor dieser Klasse erwartet keine Argumente. Durch die Methode set_payload (dt. »Nutzlast«) wird der E‐Mail ein Text hinzugefügt.
Jetzt fehlt nur noch der E‐Mail-Header. Um diesen hinzuzufügen, kann die Message-Instanz wie ein Dictionary angesprochen werden. Auf diese Weise werden die einzelnen Teile des Headers hinzugefügt. Wichtig sind dabei "Subject" für den Betreff, "From" für den Absender und "To" für den Empfänger der Mail.
Zu guter Letzt wird die entstandene E‐Mail durch die Methode as_string in einen String geschrieben und ausgegeben.[ 152 ](Mithilfe des Moduls smtplib kann der erzeugte E‐Mail-String oder die Message-Instanz direkt verschickt werden. )
Erstellen einer E‐Mail mit Anhängen
Wir haben angekündigt, dass es das Paket email ermöglicht, Binärdaten per E‐Mail zu verschicken. Dafür ist das Modul email.mime zuständig. Das folgende Beispielprogramm erstellt eine E‐Mail und fügt eine Bilddatei als Anhang ein:
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
msg = MIMEMultipart()
msg["Subject"] = "Hallo Welt"
msg["From"] = "Donald Duck <don@ld.de>"
msg["To"] = "Onkel Dagobert <d@gobert.de>"
text = MIMEText("Dies ist meine selbst erstellte E‐Mail.")
msg.attach(text)
f = open("lena.png", "rb")
bild = MIMEImage(f.read())
f.close()
msg.attach(bild)
print(msg.as_string())
Zunächst wird eine Instanz der Klasse MIMEMultipart erzeugt. Diese repräsentiert eine E‐Mail, die MIME-codierte Binärdaten enthalten kann. Wie im vorangegangenen Beispiel werden Betreff, Absender und Empfänger nach Art eines Dictionarys hinzugefügt.
Danach wird eine Instanz der Klasse MIMEText erzeugt, die den reinen Text der E‐Mail enthalten soll. Diese Instanz wird mithilfe der Methode attach an die MIMEMultipart-Instanz angehängt.
Genauso verfahren wir mit dem Bild: Es wird eine Instanz der Klasse MIMEImage erzeugt und mit den Binärdaten des Bildes gefüllt. Danach wird sie mittels attach an die E‐Mail angefügt.
Schließlich wird die MIMEMultipart-Instanz durch Aufruf der Methode as_string in einen String konvertiert, der so als reine ASCII-E‐Mail versendet werden kann. Der angefügte Anhang wird von E‐Mail-Programmen als Grafik erkannt und dann dementsprechend präsentiert. Die Ausgabe des Beispiels sieht so aus:
Content-Type: multipart/mixed; boundary="===========0094312333=="
MIME-Version: 1.0
Subject: Hallo Welt
From: Donald Duck <don@ld.de>
To: Onkel Dagobert <d@gobert.de>
--===============0094312333==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Dies ist meine selbst erstellte E‐Mail.
--===============0094312333==
Content-Type: image/png
MIME-Version: 1.0
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAg
AElEQVR4nIS8Sa9tXXYsFBFjzrXPucVXZWlnukC200bvGR6FEeJ1wAghJPgxgOjSR+JPICHRgA4N
[…]
--===============0094312333==--
Sie sehen, dass sowohl der Text als auch das Bild in ähnlicher Form codiert wurden. Die Aufbereitung der beiden Sektionen zum Textteil der E‐Mail und zu einem Bild im Anhang erledigt Ihr Mail-Programm. Das mime-Paket bietet auch eine entsprechende Funktionalität an, auf die wir noch zu sprechen kommen werden.
Tabelle 34.12 listet die verfügbaren MIME-Datentypen auf:
MIME-Datentyp | Zweck |
---|---|
email.mime.application.MIMEApplication | Programme |
email.mime.audio.MIMEAudio | Audiodateien |
email.mime.image.MIMEImage | Grafikdateien |
email.mime.message.MIMEMessage | Message-Instanzen |
email.mime.image.MIMEText | reiner Text |
Beim Instanziieren all dieser Klassen müssen Sie die jeweiligen Binärdaten bzw. den Text, den die entsprechende Instanz enthalten soll, als ersten Parameter des Konstruktors übergeben. Wichtig ist noch, dass alle hier vorgestellten Klassen von der Basisklasse Message abgeleitet sind, also über die Methoden dieser Basisklasse verfügen.
Internationale Zeichensätze
Bisher wurde besprochen, wie der MIME-Standard dazu verwendet werden kann, Binärdaten im Anhang einer E‐Mail zu versenden. Beim Text der E‐Mail waren wir aber bislang auf die Zeichen des 7-Bit-ASCII-Standards beschränkt. Die Frage ist, wie ein spezielles Encoding innerhalb einer E‐Mail verwendet werden kann. Auch dies ermöglicht der MIME-Standard. Das folgende Beispielprogramm erstellt eine einfache E‐Mail, deren Text ein Eurozeichen enthält:
from email.mime.text import MIMEText
text = "39,90\u20AC"
msg = MIMEText(text.encode("cp1252"), _charset="cp1251")
msg["Subject"] = "Hallo Welt"
msg["From"] = "Donald Duck <don@ld.de>"
msg["To"] = "Onkel Dagobert <d@gobert.de>"
print(msg.as_string())
Als Erstes erzeugen wir einen String, der das Eurozeichen enthält, das nicht Teil des ASCII-Standards ist. Im Folgenden wird der String ins Windows-Encoding cp1252 codiert und bei der Instanziierung der Klasse MIMEText übergeben. Das verwendete Encoding muss dem Konstruktor ebenfalls über den Parameter _charset bekannt gemacht werden. Der nun folgende Teil des Programms ist Ihnen bereits von den anderen Beispielen her bekannt.
Der MIME-codierte Text, den das Beispielprogramm ausgibt, sieht folgendermaßen aus:
Content-Type: text/plain; charset="cp1252"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Subject: Hallo Welt
From: Donald Duck <don@ld.de>
To: Onkel Dagobert <d@gobert.de>
MzksOTCA
Dabei entspricht MzksOTCA der MIME-Codierung des Textes 39,90€.
Es kann durchaus vorkommen, dass auch Einträge im Header der E‐Mail Sonderzeichen enthalten. Sie können mithilfe der Klasse Header codiert werden:
from email.mime.text import MIMEText
from email.header import Header
msg = MIMEText("Hallo Welt")
msg["Subject"] = Header("39,90\u20AC", "cp1252")
[…]
Eine E‐Mail einlesen
Zum Schluss möchten wir Ihnen noch ein kurzes Beispiel dazu geben, dass eine abgespeicherte E‐Mail auch wieder eingelesen und automatisch zu der bislang besprochenen Klassenstruktur aufbereitet werden kann. Dazu folgendes Beispiel:
import email
mail = """Subject: Hallo Welt
From: Donald Duck <don@ld.de>
To: Onkel Dagobert <d@gobert.de>
Hallo Welt
"""
msg = email.message_from_string(mail)
print(msg["From"])
Im Beispielprogramm ist eine E‐Mail in Form eines Strings vorhanden und wird durch die Funktion message_from_string eingelesen. Diese Funktion gibt eine Message-Instanz zurück, wie die darauffolgende print-Ausgabe beweist:
Donald Duck <don@ld.de>
Alternativ hätten wir auch die Funktion message_from_file verwenden können, um die E‐Mail aus einer Datei zu lesen. Dieser Funktion hätten wir dann ein geöffnetes Dateiobjekt übergeben müssen.