34.3 FTP – ftplib 

Das Modul ftplib ermöglicht einer Anwendung, sich mit einem FTP-Server zu verbinden und Operationen auf diesem durchzuführen. FTP steht für File Transfer Protocol und bezeichnet ein Netzwerkprotokoll, das für Dateiübertragungen in TCP/IP-Netzwerken entwickelt wurde. Gerade im Internet ist FTP verbreitet. So erfolgen beispielsweise Dateiübertragungen auf einen Webserver üblicherweise via FTP.
Das Protokoll FTP ist sehr einfach aufgebaut und besteht aus einer Reihe von Befehlen, die auch von Menschen gelesen werden können. Im Prinzip könnte man also auch direkt mit dem FTP-Server kommunizieren, ohne eine abstrahierende Bibliothek zwischenzuschalten. Tabelle 34.7 listet die wichtigsten FTP-Befehle auf und erläutert kurz ihre Bedeutung. Sie werden sehen, dass sich das Modul ftplib stark an diese Befehle anlehnt.
Befehl | Beschreibung |
---|---|
OPEN | Baut eine Verbindung zu einem FTP-Server auf. |
USER | Überträgt einen Benutzernamen zum Login an den FTP-Server. |
PASS | Überträgt ein Passwort zum Login an den FTP-Server. |
CWD | Ändert das aktuelle Arbeitsverzeichnis auf dem FTP-Server (CWD für change working directory). |
PWD | Gibt das aktuelle Arbeitsverzeichnis auf dem FTP-Server zurück (PWD für print working directory). |
DELE | Löscht eine Datei auf dem FTP-Server (DELE für delete). |
LIST LS |
Überträgt eine Liste aller im Arbeitsverzeichnis enthaltenen Dateien und Ordner. |
MKD | Erstellt ein Verzeichnis auf dem FTP-Server (MKD für make directory). |
RMD | Löscht ein Verzeichnis auf dem FTP-Server (RMD für remove directory). |
RETR | Überträgt eine Datei vom FTP-Server (RETR für retrieve). |
STOR | Überträgt eine Datei vom Client an den FTP-Server (STOR für store). |
QUIT | Beendet die Verbindung zwischen Server und Client. |
Tabelle 34.7 FTP-Befehle
Die Kommunikation mit einem FTP-Server läuft auf zwei Kanälen ab: auf dem Steuerkanal zum Senden von Befehlen an den Server und auf dem Datenkanal zum Empfangen von Daten. Diese Trennung von Kommando- und Übertragungsebene ermöglicht es, dass auch während einer laufenden Datenübertragung Befehle, beispielsweise zum Abbruch der Übertragung, an den Server gesendet werden können.
Grundsätzlich kann eine Datenübertragung in zwei Modi ablaufen: Im aktiven Modus fordert der Client eine Datei an und öffnet gleichzeitig einen Port, über den dann die Übertragung der Datei ablaufen soll. Dem gegenüber steht der passive Modus, bei dem der Client den Server instruiert, einen Port zu öffnen, um die Datenübertragung durchzuführen. Das hat den Vorteil, dass auch Datenübertragungen mit Clients stattfinden können, die für den Server nicht direkt adressierbar sind, weil sie beispielsweise hinter einem Router oder einer Firewall stehen.
Das Modul ftplib stellt die Klasse FTP zur Verfügung, die es einer Anwendung ermöglicht, sich mit einem FTP-Server zu verbinden und die dort unterstützten Operationen auszuführen. Mit diesem Modul können Sie also einen vollwertigen FTP-Client implementieren.
34.3.1 Mit einem FTP-Server verbinden 

Bereits beim Instanziieren der Klasse FTP kann eine Verbindung mit einem FTP-Server hergestellt werden. Dazu muss dem Konstruktor mindestens die Adresse des FTP-Servers als String übergeben werden. Der Konstruktor der Klasse FTP hat die im Folgenden beschriebene Schnittstelle:
FTP([host, user, passwd, acct, timeout, source_address])
Der Konstruktor erzeugt eine Instanz der Klasse FTP, die mit dem FTP-Server host verbunden ist. Bei der Anmeldung an diesem Server werden der Benutzername user und das Passwort passwd verwendet. Über den optionalen Parameter timeout wird ein Timeout-Wert in Sekunden für die Verbindungsanfrage eingestellt. Wenn Sie timeout nicht angeben, wird ein System-Default verwendet.
Über den Parameter acct (für accounting information) können weitere Informationen an den FTP-Server gesendet werden, was aber in der Regel nicht benötigt wird. Mit source_address wird die Herkunftsadresse, die bei der Verbindung verwendet werden soll, als Tupel im Format (ip, port) angegeben.
Alternativ lässt sich die FTP-Klasse parameterlos instanziieren. Dann wird die Verbindung über die Methoden connect und login hergestellt, denen jeweils die Verbindungs- bzw. Login-Daten übergeben werden müssen:
>>> import ftplib
>>> ftp = ftplib.FTP()
>>> ftp.connect("ftp.server.de")
'220 Serverantwort'
>>> ftp.login("Benutzername", "Passwort")
'230 Login successful.'
Die Methode connect unterstützt zusätzlich die optionalen Parameter port und timeout.
Die Verbindung zu einem FTP-Server kann über die parameterlosen Methoden quit und close beendet werden. Dabei trennt quit die Verbindung sauber, indem es ein QUIT-Kommando an den Server sendet und dessen Antwort abwartet, während close die Verbindung trennt, ohne den Server davon in Kenntnis zu setzen.
Um die Beispiele in den folgenden Abschnitten ausführen zu können, müssen Sie sowohl das Modul ftplib importieren als auch eine FTP-Instanz ftp erzeugen, die mit einem FTP-Server Ihrer Wahl verbunden ist:
>>> import ftplib
>>> ftp = ftplib.FTP("ftp.server.de")
>>> ftp.login("Benutzername", "Passwort")
'230 Login successful.'
[»] Hinweis
Die FTP-Klasse lässt sich mit dem with-Statement verwenden:
with ftplib.FTP("ftp.server.de") as f:
f.login("Benutzername", "Passwort")
34.3.2 FTP-Kommandos ausführen 

Die Klasse FTP definiert, wie Sie in den folgenden Abschnitten sehen werden, Methoden für die gängigsten FTP-Kommandos. Um darüber hinaus direkt mit dem FTP-Server zu kommunizieren, existiert die Methode sendcmd, die ein Kommando als String an den Server sendet und die Antwort des Servers ebenfalls als String zurückgibt.
>>> ftp.sendcmd("PWD")
'257 "/" is the current directory.'
34.3.3 Mit Dateien und Verzeichnissen arbeiten 

Analog zu den eingangs besprochenen Befehlen, die das FTP-Protokoll definiert, existieren Methoden der Klasse FTP, die grundlegende Operationen auf Dateien und Verzeichnissen durchführen. Tabelle 34.8 fasst die vorhandenen Methoden zusammen und erklärt kurz ihre Bedeutung:
Methode | Bedeutung |
---|---|
cwd(pathname) | Ändert das aktuelle Arbeitsverzeichnis in pathname. |
delete(filename) | Löscht die Datei filename. |
mkd(pathname) | Erzeugt das Verzeichnis pathname. |
mlsd([path]) | Gibt den Inhalt des aktuellen Arbeitsverzeichnisses bzw. des Verzeichnisses path zurück. |
pwd() | Gibt den Pfad des aktuellen Arbeitsverzeichnisses zurück. |
rename(fromname, toname) | Benennt die Datei fromname in toname um. |
rmd(dirname) | Löscht das Verzeichnis dirname Server. Das Verzeichnis muss vorhanden und leer sein. |
size(filename) | Ermittelt die Dateigröße der Datei filename.* |
* Beachten Sie, dass das dieser Methode zugrunde liegende FTP-Kommando SIZE nicht standardisiert ist und somit nicht von allen FTP-Servern unterstützt wird. |
Tabelle 34.8 Datei- und Verzeichnisoperationen auf einem FTP-Server
Die Methode mlsd gibt ein iterierbares Objekt zurück, das die Dateien und Unterverzeichnisse des als Parameter übergebenen Verzeichnisses path auf dem FTP-Server enthält:
>>> for x in ftp.mlsd():
... print("{}: {}".format(x[0], x[1]["type"]))
...
bild.png: file
hallo.txt: file
ordner1: dir
ordner2: dir
Jedes Element im Verzeichnis wird durch ein Tupel mit zwei Einträgen repräsentiert, das den Namen und ein Dictionary mit weiteren Attributen enthält. Im Beispiel wurde neben dem Datei- bzw. Ordnernamen der Wert des Attributs type ausgegeben, das kennzeichnet, ob es sich um eine Datei oder um ein Verzeichnis handelt.
34.3.4 Übertragen von Dateien 

Zum Austausch einer Datei mit einem FTP-Server müssen zwei grundlegende Entscheidungen getroffen werden: die Richtung des Dateitransfers – eine Datei kann gesendet oder empfangen werden – und der Übertragungsmodus. Jede der vier möglichen Kombinationen dieser Transferparameter wird in der Klasse FTP über eine Methode repräsentiert. Die Methoden heißen retrbinary, retrlines, storbinary und storlines und werden im Folgenden vorgestellt.
Die Methode set_pasv versetzt die FTP-Instanz, abhängig vom übergebenen booleschen Parameter, in den aktiven bzw. passiven Modus. Im aktiven Zustand muss der Client für den Server erreichbar sein, darf sich also nicht hinter einer Firewall oder einem Router befinden.
[»] Hinweis
Die parameterlose Methode abort unterbricht einen laufenden Datentransfer. Je nach Server kann eine solche Unterbrechung nicht zu jedem Zeitpunkt durchgeführt werden.
retrbinary(cmd, callback, [maxblocksize, rest])
Diese Methode leitet einen Datentransfer im Binärmodus ein. Dazu muss als erster Parameter ein entsprechendes FTP-Kommando übergeben werden, aufgrund dessen der Server einen Datentransfer über den Datenkanal startet. Für einen simplen Dateitransfer wird das Kommando RETR dateiname verwendet.
An zweiter Stelle muss ein Funktionsobjekt übergeben werden. Die dahinterstehende Funktion muss exakt einen Parameter akzeptieren. Nach jedem erfolgreich übermittelten Block wird die Funktion callback aufgerufen. Die übertragenen Binärdaten werden dabei als Parameter in Form eines bytes-Strings übergeben.
Der Parameter maxblocksize bestimmt die maximale Größe der Blöcke, in die die Datei zum Herunterladen aufgeteilt wird.
Über den vierten, optionalen Parameter rest wird ein Offset in der zu übertragenden Datei angegeben, ab dem der Server den Dateiinhalt senden soll. Dies ist zum Beispiel nützlich, um abgebrochene Downloads wieder aufzunehmen, ohne dabei Teile der Datei doppelt herunterladen zu müssen.
Zur Verwendung von retrbinary nun folgendes Beispiel:
>>> class Downloader:
... def __init__(self):
... self.data = bytes()
... def __call__(self, data):
... self.data += data
... bild = Downloader()
... ftp.retrbinary("RETR bild.png", bild)
...
>>> len(bild.data)
473831
Das Beispielprogramm lädt die Bilddatei bild.png aus dem aktuellen Arbeitsverzeichnis des FTP-Servers herunter und speichert die Binärdaten im bytes-String bild.data. Um zum Speichern der Daten nicht auf eine globale Referenz zurückgreifen zu müssen, haben wir eine Klasse Downloader erstellt, die sich mithilfe der Magic Method __call__ wie eine Funktion aufrufen lässt.
Alternativ kann auch ein LIST-Kommando abgesetzt werden. Der Verzeichnisinhalt wird vom Server ebenfalls über den Datenkanal gesendet.
>>> def f(data):
... print(data.decode())
...
>>> ftp.retrbinary("LIST", f)
-rw-r--r-- 1 peter users 473831 Mar 07 17:42 bild.png
-rw-r--r-- 1 peter users 35 Mar 07 17:55 hallo.txt
drwxr-xr-x 2 peter users 4096 Mar 07 17:39 ordner1
drwxr-xr-x 2 peter users 4096 Mar 07 17:39 ordner2
'226 Transfer complete.'
retrlines(command, [callback])
Diese Methode leitet einen Dateitransfer im ASCII-Modus ein. Dazu müssen Sie als ersten Parameter ein entsprechendes FTP-Kommando übergeben. Für einen simplen Dateitransfer wäre dies RETR dateiname. Möglich ist aber beispielsweise auch, den Inhalt des Arbeitsverzeichnisses durch ein LIST-Kommando zu übertragen.
Eine Dateiübertragung im ASCII-Modus erfolgt zeilenweise. Das heißt, die Callback-Funktion callback wird nach jeder vollständig übertragenen Zeile aufgerufen. Sie bekommt dabei die gelesene Zeile als Parameter übergeben. Beachten Sie, dass das abschließende Newline-Zeichen nicht mit übergeben wird.
Wenn Sie keine Callback-Funktion angegeben haben, werden die übertragenen Daten ausgegeben.
>>> class Downloader:
... def __init__(self):
... self.lines = []
... def __call__(self, line):
... self.lines.append(line)
...
>>> text = Downloader()
>>> ftp.retrlines("RETR hallo.txt", text)
'226 Transfer complete.'
>>> print("\n".join(text.lines))
Dies ist der Inhalt von hallo.txt
Dieses Beispielprogramm lädt die Textdatei text.txt zeilenweise herunter und fügt die heruntergeladenen Zeilen im String text wieder zu einem Gesamttext zusammen. Dabei bedienen wir uns wieder der aufrufbaren Klasse Downloader, um die Zwischenergebnisse zu speichern.
storbinary(command, file, [blocksize, callback, rest])
Diese Methode leitet einen Datei-Upload ein. Dabei muss als erster Parameter ein entsprechender FTP-Befehl in Form eines bytes-Strings übergeben werden. Für einen simplen Datei-Upload lautet dieser Befehl STOR dateiname, wobei dateiname der Zielname der Datei auf dem FTP-Server ist. Als zweiten Parameter müssen Sie ein im Binärmodus geöffnetes Dateiobjekt übergeben, dessen Inhalt hochgeladen werden soll.
Optional kann in Form des dritten Parameters, blocksize, die maximale Größe der Datenblöcke angegeben werden, in denen die Datei hochgeladen wird.
Wenn für den vierten Parameter callback das Funktionsobjekt einer Funktion mit einem Parameter übergeben wird, wird diese Funktion nach jedem gesendeten Block gerufen. Dabei bekommt sie die gesendeten Daten als bytes-String übergeben. Der letzte Parameter rest hat die gleiche Bedeutung wie bei retrbinary.
Das folgende Beispielprogramm führt einen binären Datei-Upload durch:
f = open("hallo.png", "rb")
ftp.storbinary("STOR bild.png", f)
f.close()
Die Datei heißt im lokalen Arbeitsverzeichnis hallo.png, wird jedoch unter dem Namen bild.png hochgeladen.
storlines(command, file, [callback])
Diese Methode verhält sich ähnlich wie storbinary – mit dem Unterschied, dass die Datei im ASCII-Modus zeilenweise hochgeladen wird. Die Parameter command, file und callback lassen sich wie bei storbinary verwenden.
Beachten Sie, dass Sie das für file übergebene Dateiobjekt wie bei storbinary auch im Binärmodus geöffnet haben müssen.