2010-04-09 11 views
7

Questo non scaricherà il contenuto delle sottodirectory; come posso farlo?Download di un albero di directory con 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() 

risposta

6

questa è un'alternativa. puoi provare a utilizzare il pacchetto ftputil. È quindi possibile utilizzarlo per walk the remote directories e ottenere i file

+0

è davvero così complicato al punto in cui dovrei essere pacchetti in considerazione? Sto pensando che prenderà solo 10 righe di codice veramente complicato. –

1

Non è banale almeno. Nel caso più semplice, si presuppone solo di avere file e directory. Questo non è sempre il caso, ci sono collegamenti softlink e hardlink e stile Windows. Le scorciatoie di Softlink e directory sono particolarmente problematiche poiché rendono possibile la directory ricorsiva, il che confonderebbe in modo ingenuo l'uso di ftp grabber.

Come gestireste tale directory ricorsiva in base alle vostre esigenze; potresti semplicemente non seguire i softlink o provare a rilevare i collegamenti ricorsivi. Rilevare il link ricorsivo è intrinsecamente ingannevole, non puoi farlo in modo affidabile.

10

questo dovrebbe fare il trucco :)

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

inoltre, potresti non voler uscire nel primo 'ftplib.error_perm' salvo nel caso in cui il tuo account utente abbia problemi di autorizzazione. 'pass' potrebbe funzionare. – jameh

6

Questa è una domanda molto vecchio, ma ho avuto un bisogno simile che ho voluto per soddisfare in modo molto generale. Ho finito per scrivere la mia soluzione che funziona molto bene per me. L'ho inserito su Gist qui https://gist.github.com/Jwely/ad8eb800bacef9e34dd775f9b3aad987

e incollato di seguito nel caso in cui io abbia mai preso la sostanza offline.

Esempio di utilizzo:

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

Il codice qui sopra per cercare una directory chiamata "remote_dir" sull'host FTP, e quindi duplicare la directory e tutto il suo contenuto nella "local_dir". Invoca lo script seguente.

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

Fantastico. Funziona come un fascino. Dovrebbe essere una biblioteca per questo! –

3

Utilizzando ftputil, una soluzione veloce potrebbe essere:

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)) 
Problemi correlati