2010-04-09 12 views
7

Dies wird nicht den Inhalt von Unterverzeichnissen herunterladen; Wie kann ich das tun?Herunterladen eines Verzeichnisbaums mit ftplib

import ftplib 
import configparser 
import os 

directories = [] 

def add_directory(line): 
if line.startswith('d'): 
    bits = line.split() 
    dirname = bits[8] 
    directories.append(dirname) 

def makeDir(archiveTo): 
for dir in directories: 
    newDir = os.path.join(archiveTo, dir) 
    if os.path.isdir(newDir) == True: 
    print("Directory \"" + dir + "\" already exists!") 
    else: 
    os.mkdir(newDir) 

def getFiles(archiveTo, ftp): 
files = ftp.nlst() 
for filename in files: 
    try: 
    directories.index(filename) 
    except: 
    ftp.retrbinary('RETR %s' % filename, open(os.path.join(archiveTo, filename), 'wb').write) 

def runBackups(): 

#Load INI 
filename = 'connections.ini' 
config = configparser.SafeConfigParser() 
config.read(filename) 

connections = config.sections() 
i = 0 

while i < len(connections): 
    #Load Settings 
    uri = config.get(connections[i], "uri") 
    username = config.get(connections[i], "username") 
    password = config.get(connections[i], "password") 
    backupPath = config.get(connections[i], "backuppath") 
    archiveTo = config.get(connections[i], "archiveto") 

    #Start Back-ups 
    ftp = ftplib.FTP(uri) 
    ftp.login(username, password) 
    ftp.cwd(backupPath) 

    #Map Directory Tree 
    ftp.retrlines('LIST', add_directory) 

    #Make Directories Locally 
    makeDir(archiveTo) 

    #Gather Files 
    getFiles(archiveTo, ftp) 

    #End connection and increase counter. 
    ftp.quit() 
    i += 1 

print() 
print("Back-ups complete.") 
print() 

Antwort

6

Dies ist eine Alternative. Sie können versuchen, ftputil Paket zu verwenden. Sie können es dann zu walk the remote directories verwenden und erhalten Sie Ihre Dateien

+0

Ist es wirklich so kompliziert zu dem Punkt, wo ich unter Berücksichtigung Pakete enthalten sein sollte? Ich denke, es wird nur 10 Zeilen wirklich komplizierten Code benötigen. –

1

Es ist mindestens nicht trivial. Im einfachsten Fall gehen Sie nur davon aus, dass Sie Dateien und Verzeichnisse haben. Dies ist nicht immer der Fall, es gibt Softlinks und Hardlinks und eine Verknüpfung im Windows-Stil. Softlink und Verzeichnisverknüpfung sind besonders problematisch, da sie ein rekursives Verzeichnis ermöglichen, was einen naiv implementierten ftp-Grabber verwirren würde.

Wie würden Sie mit solch rekursiven Verzeichnissen umgehen, hängt von Ihren Bedürfnissen ab; Sie könnten einfach nicht weichen Links folgen oder Sie könnten versuchen, rekursive Links zu erkennen. Das Erkennen rekursiver Links ist von Natur aus schwierig, Sie können es nicht zuverlässig tun.

10

dies sollte es tun :)

import sys 
import ftplib 
import os 
from ftplib import FTP 
ftp=FTP("ftp address") 
ftp.login("user","password") 

def downloadFiles(path,destination): 
#path & destination are str of the form "/dir/folder/something/" 
#path should be the abs path to the root FOLDER of the file tree to download 
    try: 
     ftp.cwd(path) 
     #clone path to destination 
     os.chdir(destination) 
     os.mkdir(destination[0:len(destination)-1]+path) 
     print destination[0:len(destination)-1]+path+" built" 
    except OSError: 
     #folder already exists at destination 
     pass 
    except ftplib.error_perm: 
     #invalid entry (ensure input form: "/dir/folder/something/") 
     print "error: could not change to "+path 
     sys.exit("ending session") 

    #list children: 
    filelist=ftp.nlst() 

    for file in filelist: 
     try: 
      #this will check if file is folder: 
      ftp.cwd(path+file+"/") 
      #if so, explore it: 
      downloadFiles(path+file+"/",destination) 
     except ftplib.error_perm: 
      #not a folder with accessible content 
      #download & return 
      os.chdir(destination[0:len(destination)-1]+path) 
      #possibly need a permission exception catch: 
      ftp.retrbinary("RETR "+file, open(os.path.join(destination,file),"wb").write) 
      print file + " downloaded" 
    return 

source="/ftproot/folder_i_want/" 
dest="/systemroot/where_i_want_it/" 
downloadFiles(source,dest) 
+0

auch, möchten Sie nicht in der ersten 'ftplib.error_perm 'beenden, außer für den Fall, dass Ihr Benutzerkonto Berechtigungsprobleme hat. 'pass' könnte funktionieren. – jameh

6

Dies ist eine sehr alte Frage, aber ich hatte ein ähnliches Bedürfnis, das ich in sehr allgemeiner Weise befriedigen wollte. Ich habe meine eigene Lösung geschrieben, die sehr gut für mich funktioniert. Ich habe es auf Gist hier platziert https://gist.github.com/Jwely/ad8eb800bacef9e34dd775f9b3aad987

und fügte es unten für den Fall, dass ich jemals den Kern offline nehmen.

Beispiel Nutzung:

import ftplib 
ftp = ftplib.FTP(mysite, username, password) 
download_ftp_tree(ftp, remote_dir, local_dir) 

Der obige Code für ein Verzeichnis aussehen wird „Remote_Dir“ auf dem FTP-Host genannt, und dann kopieren Sie das Verzeichnis und seinen gesamten Inhalt in die „local_dir“. Es ruft das folgende Skript auf.

import ftplib 
import os 

def _is_ftp_dir(ftp_handle, name, guess_by_extension=True): 
    """ simply determines if an item listed on the ftp server is a valid directory or not """ 

    # if the name has a "." in the fourth to last position, its probably a file extension 
    # this is MUCH faster than trying to set every file to a working directory, and will work 99% of time. 
    if guess_by_extension is True: 
     if name[-4] == '.': 
      return False 

    original_cwd = ftp_handle.pwd()  # remember the current working directory 
    try: 
     ftp_handle.cwd(name)   # try to set directory to new name 
     ftp_handle.cwd(original_cwd) # set it back to what it was 
     return True 
    except: 
     return False 


def _make_parent_dir(fpath): 
    """ ensures the parent directory of a filepath exists """ 
    dirname = os.path.dirname(fpath) 
    while not os.path.exists(dirname): 
     try: 
      os.mkdir(dirname) 
      print("created {0}".format(dirname)) 
     except: 
      _make_parent_dir(dirname) 


def _download_ftp_file(ftp_handle, name, dest, overwrite): 
    """ downloads a single file from an ftp server """ 
    _make_parent_dir(dest) 
    if not os.path.exists(dest) or overwrite is True: 
     with open(dest, 'wb') as f: 
      ftp_handle.retrbinary("RETR {0}".format(name), f.write) 
     print("downloaded: {0}".format(dest)) 
    else: 
     print("already exists: {0}".format(dest)) 


def _mirror_ftp_dir(ftp_handle, name, overwrite, guess_by_extension): 
    """ replicates a directory on an ftp server recursively """ 
    for item in ftp_handle.nlst(name): 
     if _is_ftp_dir(ftp_handle, item): 
      _mirror_ftp_dir(ftp_handle, item, overwrite, guess_by_extension) 
     else: 
      _download_ftp_file(ftp_handle, item, item, overwrite) 


def download_ftp_tree(ftp_handle, path, destination, overwrite=False, guess_by_extension=True): 
    """ 
    Downloads an entire directory tree from an ftp server to the local destination 

    :param ftp_handle: an authenticated ftplib.FTP instance 
    :param path: the folder on the ftp server to download 
    :param destination: the local directory to store the copied folder 
    :param overwrite: set to True to force re-download of all files, even if they appear to exist already 
    :param guess_by_extension: It takes a while to explicitly check if every item is a directory or a file. 
     if this flag is set to True, it will assume any file ending with a three character extension ".???" is 
     a file and not a directory. Set to False if some folders may have a "." in their names -4th position. 
    """ 
    os.chdir(destination) 
    _mirror_ftp_dir(ftp_handle, path, overwrite, guess_by_extension) 
+0

Ehrfürchtig. Klappt wunderbar. Sollte dafür eine Bibliothek sein! –

3

ftputil benutzen, eine schnelle Lösung könnte sein:

def download(folder): 
    for item in ftp.walk(folder): 
     print("Creating dir " + item[0]) 
     os.mkdir(item[0]) 
     for subdir in item[1]: 
      print("Subdirs " + subdir) 
     for file in item[2]: 
      print(r"Copying File {0} \ {1}".format(item[0], file)) 
      ftp.download(ftp.path.join(item[0],file), os.path.join(item[0],file)) 
Verwandte Themen