2010-04-09 23 views
7

Esto no descargará el contenido de los subdirectorios; ¿como lo puedo hacer?Descargar un árbol de directorios 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() 

Respuesta

6

esta es una alternativa. puede intentar usar el paquete ftputil. A continuación, puede usarlo en walk the remote directories y obtener sus archivos

+0

¿Es realmente tan complicado hasta el punto donde debería estar considerando paquetes? Estoy pensando que solo tomará 10 líneas de código realmente complicado. –

1

No es trivial al menos. En el caso más simple, solo asumes que tienes archivos y directorios. Este no es siempre el caso, hay softlinks y enlaces duros y acceso directo al estilo de Windows. Softlink y el acceso directo a directorios son particularmente problemáticos ya que hacen que el directorio recursivo sea posible, lo que podría confundir al capturador de ftp implementado de forma ingenua.

¿Cómo manejarías ese directorio recursivo depende de tu necesidad? es posible que simplemente no sigas softlinks o intentes detectar enlaces recursivos. La detección de enlaces recursivos es intrínsecamente engañosa, no se puede hacer de manera confiable.

10

esto debe hacer el truco :)

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

también, es posible que no desee salir en el primer 'ftplib.error_perm' excepto en el caso de que su cuenta de usuario tenga problemas de permisos. 'pass' podría funcionar. – jameh

6

Ésta es una pregunta muy antigua, pero no tenía una necesidad similar que quería satisfacer de una manera muy general. Terminé escribiendo mi propia solución que funciona muy bien para mí. Lo he colocado en Gist aquí https://gist.github.com/Jwely/ad8eb800bacef9e34dd775f9b3aad987

y lo pegué debajo en caso de que alguna vez lo saque de la red.

Ejemplo de uso:

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

El código anterior buscará un directorio llamado "dir_remoto" en el host ftp, y luego duplicar el directorio y todo su contenido en el "local_dir". Invoca el script a continuación.

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

Impresionante. Funciona de maravilla. ¡Debería ser una biblioteca para esto! –

3

Usando ftputil, una solución rápida podría ser:

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)) 
Cuestiones relacionadas