Python bietet darüber hinausgehende Bibliotheken an, um Standardzugriffe zu vereinfachen. So gibt es Module, um beispielsweise mit Webservern oder Mailservern Kontakt aufzunehmen. Das erspart dem Programmierer das Zusammensetzen der Befehlspakete und das Zerpflücken der Antwortpakete und macht so auch die Programmierung deutlich übersichtlicher.
Grundlegend: Socketprogrammierung
Eine Netzwerkkommunikation findet typischerweise immer zwischen einem Prozess, der bereitsteht, und einem Prozess, der Anfragen stellt, statt. Der wartende Prozess wird Server genannt, der Anfrager Client.Damit sich beide finden, müssen der Client zunächst wissen, auf welchem Computer sein Partner ist. Dazu muss er eine beim Start eine IP-Nummer oder einen Hostname angeben.
Kennt der Client den Computer des Servers, muss er den Prozess kennen, mit dem er sprechen will. Dazu wird ein sogenannter Port festgelegt, an dem der Server auf ihn wartet. Weil dieser Port dem Client vor der Kontaktaufnahme bekannt ist, spricht man von einem well known port. Die Ports eines Computers sind durchnummeriert. Ein normaler Webserver verwendet beispielsweise den Port 80.
Die Kontaktpunkte werden Socket genannt. Der Server bindet seinen Socket an den well known port und wartet auf Verbindungen. Der Client verbindet seinen Socket mit dem Port des Servers. Anschließend werden über read() und write() Daten getauscht wie über Dateien.
Der Server
Der Server soll seine Dienste über Port 50000 anbieten. Das ist reichlich hoch, sollte aber genau darum auch in der Regel frei sein. Es kann pro Computer nämlich nur einen geben, der auf einem bestimmten Port seine Dienste anbietet.Anschließend geht der Server in eine Endlosschleife. In dieser wird zunächst recv() aufgerufen. Dies blockiert den Prozess solange, bis ein Client eine Verbindung aufnimmt und Daten schickt.
Sobald dies passiert, wird der Server die Daten verarbeiten, die Antwort zurücksenden und auf neue Anfragen warten. In unserem Beispiel werden die Clientdaten einfach auf dem Bildschirm ausgegeben und dem Client wird einen Gruß zugesandt.
import socket MeinSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: MeinSocket.bind(("", 50000)) MeinSocket.listen(1) while True: PartnerSocket, addr = MeinSocket.accept() while True: daten = PartnerSocket.recv(1024) if not daten: PartnerSocket.close() break print("[%s] %s" % (addr[0], daten)) PartnerSocket.send("Guten Tag") finally: MeinSocket.close()
Der Client
Der Client durchläuft keine Schleife, sondern verbindet sich über die Funktion connect() mit dem Server. Der Parameter von connect() ist ein Tupel, das aus Rechneradresse und Port besteht. Darum sind an dieser Stelle auch doppelte Klammern erforderlich. Die Adresse 127.0.0.1 ist immer der localhost, also der eigene Rechner. Wenn Sie die Verbindung unbedingt über das Netzwerk austesten wollen, müssen Sie die Adresse des Server-Computers eintragen. Die Portnummer muss natürlich die sein, unter der der Serverprozess gebunden ist.Nun sendet der Client seine Daten und ruft gleich anschließend die Funktion recv() auf, die den Client blockiert, bis der Server eine Antwort sendet. Diese Antwort gibt er auf dem Bildschirm aus und endet.
import socket MeinSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: MeinSocket.connect(("127.0.0.1", 50000)) MeinSocket.send("Hallo du da") Daten = MeinSocket.recv(1024) print("%s" % (Daten)) finally: MeinSocket.close()Prinzipiell können Sie auf dieser Basis eine eigene Netzwerkkommunikation aufbauen. Bei parallelen Zugriffen sollten Sie das Kapitel~thread zurate ziehen. Sie können mit dem Client-Grundgerüst auch durchaus Web-, Mail- und andere Server kontaktieren. In diesen Fällen ist es aber einfacher und sicherer, wenn Sie die Module verwenden, die Ihnen Python anbietet.
Versenden von Mails: SMTP
Es kommt immer häufiger vor, dass Programme Mails versenden. Das betrifft natürlich auch Spam, also ungefragte und damit unerlaubte Werbebotschaften. Aber auch ganz legale Anwendungen versenden Rechnungen, Angebote oder Anfragen direkt per Mail.Um eine Mail zu versenden, wendet man sich an einen SMTP-Server. SMTP steht für Simple Mail Transfer Protocol. Für das Versenden von Mails bietet Python as Modul smtplib an.
Die grundlegende Abfolge ist zunächst recht simpel. Es wird ein Server-Objekt über den Konstruktor smtplib.SMTP() erzeugt, der den Servernamen als Parameter übernimmt. Über dieses Objekt werden die folgenden Funktionen nacheinander aufgerufen.
- login(benutzername, passwort)
Die Methode login() wird mit Benutzername und Passwort aufgerufen. - fehlversuche = sendmail(absender, empfaenger, Nachricht)
Die Mailadressen werden als Parameter übergeben. Der dritte Paramter ist die Nachricht als Zeichenkette. Diese allerdings hat es in sich, wie Sie noch sehen werden. Der Rückgabewert ist ein Dictionary, das alle Absender enthält, die der Server abgewiesen hat. - quit()
Zu guter Letzt beendet quit() den Kontakt zum Server.
Problemfeld Nachricht
Der dritte Parameter von sendmail() ist zwar eine reine Zeichenkette. Sie müsste aber normgerecht ausgeführt sein, damit alle Beteiligten der Sendungskette den Inhalt korrekt verstehen. Diese enthält nämlich beispielsweise auch den Betreff und weitere Einträge.Um dies zu vereinfachen, stellt Python das Modul email.message zur Verfügung. Die darin enthaltene Klasse Message ermöglicht den Eintrag der Felder Subject (Betreff), From (Absender) und To (Empfänger).
Die letzten beiden Felder werden zwar schon von sendmail() als Parameter erwartet. Dort sind es aber die nackten E-Mail-Adressen, hier die Einträge, die Sie typischerweise in Ihrem Mail-Programm sehen.
Die Methode set_payload() übernimmt nun den tatsächlichen Nachrichteninhalt an die Mail. Und der dritte Parameter der Funktion sendmail() kann erstellt werden, in dem Sie die Methode as_string() aufrufen.
Das folgende kleine Programm erfüllt nun all die oben genannten Bedingungen.
import smtplib from email.message import Message absender = "absender@provider.de" password = "dasistjasogeheim" empfaenger = "emfaenger@sonstwo.de" try: server = smtplib.SMTP('smtp.1und1.de') server.starttls() # verschluesselt senden server.ehlo() # Hallo sagen! server.login(absender, password) # Nachricht erstellen mail = Message() mail.set_payload("Dies ist der Nachrichteninhalt.") mail["Subject"] = "Der Betreff" mail["From"] = "mir" mail["To"] = "dir " server.sendmail(absender, empfaenger, mail.as_string()) server.quit() except SMTPException: print("SMTP-Fehler")
Ein Anhang namens MIME
Das Versenden einer Textnachricht ist oft nicht ausreichend. Rechnungen werden beispielsweise gern in den Anhang gestellt. Manche Behörden gehen teilweise so weit, dass sie gar nichts mehr in den Mailtext schreiben, nur damit ihre großzügigen PDF-Anhänge so richtig zur Geltung kommen.Ursprünglich war es bei Mails nicht vorgesehen, irgendwelche binären Daten zu versenden. Darum sind nicht alle Bytekombinationen zulässig. Damit man dennoch Bilder senden kann, wurde MIME erfunden. Damit werden Anhänge möglich und durch eine geschickte Kodierung kann man sogar binäre Daten senden. Allerdings muss man sie vor dem Senden um etwa ein Drittel aufpumpen.
Zunächst benötigen Sie eine ganze Reihe von Importen, wie Sie unten im Listing sehen können. Der Vorgang des eigentlichen Sendens hat sich nicht verändert, sondern lediglich das Mail-Inhaltsobjekt.
Statt einer einfachen Message() wird nun eine MIMEMultipart() erzeugt. Die Felder für Betreff, Empfänger und Absender haben sich nicht geändert. Das Textfeld, das zuvor mit set_payload() belegt wurde, wird nun mit mail.attach(MIMEText()) angehängt. Dann wird für jede Datei ein neuer MIME-Part geschaffen und mit dem Encoder kodiert. Das Listing ist größer geworden. Die Mail aber auch.
import smtplib from email.message import Message from email.mime.MIMEMultipart import MIMEMultipart from email.mime.MIMEBase import MIMEBase from email.mime.MIMEText import MIMEText from email import encoders absender = "absender@provider.de" password = "dasistjasogeheim" empfaenger = "emfaenger@sonstwo.de" server = smtplib.SMTP('smtp.1und1.de') server.starttls() # verschluesselt senden server.ehlo() # Hallo sagen! server.login(absender, password) # mail als MIME erstellen mail = MIMEMultipart() mail["Subject"] = "Der Betreff" mail["From"] = "mir <absender@provider.de>" mail["To"] = "dir <emfaenger@sonstwo.de>" # Den Nachrichteninhalt muss man nun anhaengen mail.attach( MIMEText("Dies ist der neue Nachrichteninhalt") ) # fuer jeden Anhang wiederholen dateiname = "AW.jpg" part = MIMEBase('application', "octet-stream") part.set_payload( open(dateiname,"rb").read() ) encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % dateiname) mail.attach(part) server.sendmail(absender, empfaenger, mail.as_string()) server.quit()Wenn Sie ein paar Experimente mit SMTP machen, werden Sie vielleicht aus Versehen lernen, wie leicht es ist, einen E-Mail-Absender zu fälschen und darum in Zukunft etwas vorsichtiger mit Mails umgehen, die einen scheinbar vertrauenswürdigen Absender haben.
Auslesen von Mails per POP3
Das Auslesen von E-Mails erfolgt nicht über einen SMTP-Server, sondern über die Protokolle IMAP oder POP3. IMAP ist das gängige Protokoll für E-Mail-Clients mit einer Flatrate, weil dann die Mails auf dem Server verbleiben und dort bearbeitet werden. Dies ist allerdings für Programme normalerweise weniger interessant. Stattdessen wird man eher POP3 verwenden, da die Handhabung aus Programmsicht einfacher ist und die Zugriffe normalerweise nur kurzzeitig erfolgen sollen.Für den Umgang mit POP3 stellt Python die Bibliothek poplib zur Verfügung. Der Aufruf von POP3() erwartet den Servernamen als Parater. Optional können Sie als zweiten Parameter den Port angeben, falls der Server sich nicht an die Standard-Ports hält.
Heutzutage ist es fast bei allen Servern üblich, den Datenverkehr beim Abholen der Mails zu verschlüsseln und dazu TLS anzubieten. Der Vorgänger von TLS hieß SSL und darum heißt der Konstruktor auch POP3_SSL().
Sie erhalten ein Objekt, über das Sie die Funktionen des POP3-Servers ansprechen können. Diese Funktionen entsprechen den Protokollspezifikationen der RFC 1725, die das POP3-Protokoll beschreibt.
Die folgende Aufstellung geht davon aus, dass das Ergebnis des Aufrufs von POP3() respektive POP3_SSL() in der Variablen pop abgelegt wurde.
- pop.user(benutzername)
Zum Abholen von Mails muss sich das Programm authentifizieren. Die Funktionen user() erwartet den Benutzernamen als Parameter. Oftmals entspricht dies Ihrer E-Mail-Adresse. - pop.pass(passwort)
Das Passwort wird als Parameter übergeben. Wenn es sich nicht um einen SSL/TLS-Server handelt, wird das Passwort unverschlüsselt übertragen. Das ist keine gute Idee, wenn man ein öffentliches Funknetz verwendet. - tupel = pop.stat()
Der Status des Postfachs in einem Tupel mit zwei Elementen. Das erste gibt die Anzahl der Nachrichten und das zweite den Umfang der Nachrichten an. - mailliste = pop.list()
Die Funktion holt die Liste aller vorhandenen Mails. Diese ist ein Tupel aus drei Elementen:- mailliste[0] ist die Statusmeldung des Server, meist: '+OK'
- mailliste[1] ist eine Liste mit den Mailnummern.
- mailliste[2] ist die Anzahl der vorhandenen Mails
- mail = pop.retr(mailnummer)
Die Funktion holt die spezifizierte Mail vom Server ab. Die Rückgabe ist ein Tupel. Das zweite Element des Tupels enthält eine Liste aller Zeilen der Mail. - mail = pop.top(mailnummer, zeilenzahl)
Die Funktion arbeitet ähnlich wie retr(), allerdings werden nur die ersten Zeilen ausgelesen, nicht die ganze Nachricht. - pop.dele(mailnummer)
Damit wird eine Mail auf dem Server gelöscht. Der Server sammelt die Löschanträge bis zum Aufruf von quit, also der Abmeldung. Bis dahin kann noch rset aufgerufen werden. - rset()
Verwirft die bisherigen Löschaufträge. - quit()
Beendet die Kommunikation mit dem Mailserver protokollgerecht und führt alle per dele() angemeldeten Löschaufträge aus.
#!/usr/bin/python import poplib #pop3Server = poplib.POP3('pop.gmx.de') pop3Server = poplib.POP3_SSL('pop.1und1.de') pop3Server.user('absender@provider.de') pop3Server.pass_('sowasvongeheim') # Hole die Liste aller Mails. mailliste = pop3Server.list() # Hole nur die letzten 5 Mails anzahl = len(mailliste[1]) start = anzahl-5 if start<0: start = 0 # Ausgabe der Mails for i in range(start, anzahl): mailnr = mailliste[1][i] # lines = pop3Server.top(i+1, 10)[1] lines = pop3Server.retr(i+1)[1] for line in lines: print(line) pop3Server.quit()
Griff ins WWW
Den Zugriff auf Websites erleichtert die Bibliothek urllib. Sie bietet mit urlopen eine Funktion, die eine Website öffnet wie eine Datei. Weitere Funktionen haben sogar die gleichen Namen wie ihre Gegenstücke beim Umgang mit Dateien, so dass der Programmierer sich gleich zu Hause fühlt.import urllib try: website = urllib.urlopen("http://www.willemer.de") Datei = open("index.htm", "w") Zeile = "irgendwas" while Zeile: Zeile = website.readline() Datei.write(Zeile) except: print("Datei Schreibfehler") website.close() Datei.close()