Подтвердить что ты не робот

Python-FTP загружает все файлы в каталог

Я собираю скрипт для загрузки всех файлов из каталога через FTP. До сих пор мне удалось подключиться и получить один файл, но я не могу заставить его работать в пакетном режиме (получить все файлы из каталога). Вот что у меня есть:

from ftplib import FTP
import os, sys, os.path

def handleDownload(block):
    file.write(block)
    print ".",

ddir='C:\\Data\\test\\'
os.chdir(ddir)
ftp = FTP('test1/server/')

print 'Logging in.'
ftp.login('user1\\anon', 'pswrd20')
directory = '\\data\\test\\'

print 'Changing to ' + directory
ftp.cwd(directory)
ftp.retrlines('LIST')

print 'Accessing files'

for subdir, dirs, files in os.walk(directory):
    for file in files: 
        full_fname = os.path.join(root, fname);  
        print 'Opening local file ' 
        ftp.retrbinary('RETR C:\\Data\\test\\' + fname,
                       handleDownload,
                       open(full_fname, 'wb'));
        print 'Closing file ' + filename
        file.close();
ftp.close()

Могу поспорить, что вы можете сказать, что он мало что делает, когда я запускаю его, поэтому любые предложения по улучшению будут с благодарностью.

4b9b3361

Ответ 1

Мне удалось взломать это, так что теперь размещаем соответствующий бит кода для будущих посетителей:

filenames = ftp.nlst() # get filenames within the directory
print filenames

for filename in filenames:
    local_filename = os.path.join('C:\\test\\', filename)
    file = open(local_filename, 'wb')
    ftp.retrbinary('RETR '+ filename, file.write)

    file.close()

ftp.quit() # This is the "polite" way to close a connection

Это работало для меня на Python 2.5, Windows XP.

Ответ 2

Если это просто проблема, которую вы хотите решить, я могу предложить команду wget:

cd c:\destination
wget --mirror --continue --no-host-directories --user=username --password=s3cr3t ftp://hostname/source/path/

Параметр --continue может быть очень опасным, если на сервере меняются файлы. Если файлы только добавляются, то это очень дружелюбно.

Однако, если это упражнение для вас, и вы хотите, чтобы ваша программа работала, я думаю, вам стоит начать с этой строки:

for subdir, dirs, files in os.walk(directory):

directory был удаленным исходным каталогом в большинстве ваших программ, но функция os.walk() не может перемещаться по удаленной директории. Вам нужно выполнить итерацию по возвращенным файлам, используя обратный вызов, который был отправлен в функцию retrlines.

Посмотрите на опции MLSD или NLST вместо LIST, они, вероятно, будут легче разбираться. (Обратите внимание, что FTP фактически не указывает, как выглядят списки, он всегда должен был управляться человеком на консоли или передаваться определенным именем файла. Таким образом, программы, которые делают умные вещи с перечислениями FTP, представляют их пользователю графический интерфейс, вероятно, должен иметь огромные кучи кода специального кода для странных или неясных серверов. И они, вероятно, все делают что-то глупое, когда сталкиваются с именами вредоносных файлов.)

Можете ли вы использовать sftp вместо этого? sftp имеет спецификацию того, как предполагается, что списки файлов анализируются, не передает имя пользователя/пароль в ясности и не имеет гигантского раздражения пассивных и активных подключений - он просто использует одно соединение, что означает, что он работает через большее количество брандмауэров, чем FTP.

Изменить: вам нужно передать объект 'callable' в функцию retrlines. Вызываемый объект является либо экземпляром класса, который определяет метод __call__, либо функцию. Хотя функция может быть проще описать, экземпляр класса может быть более полезным. (Вы можете использовать экземпляр для сбора имен файлов, но функция должна была бы записываться в глобальную переменную. Плохо.)

Здесь один из простейших вызываемых объектов:

>>> class c:
...  def __call__(self, *args):
...   print(args)
...
>>> f = c()
>>> f('hello')
('hello',)
>>> f('hello', 'world')
('hello', 'world')

Создает новый класс c, который определяет метод экземпляра __call__. Это просто печатает свои аргументы довольно глупо, но это показывает, насколько мы минимальны.:)

Если вы хотите что-то умнее, он может сделать что-то вроде этого:

class handle_lines:
  def __init__(self):
    self.lines = []
  def __call__(self, *args):
    self.lines << args[0]

Вызовите iterlines с объектом этого класса, а затем просмотрите элемент lines для деталей.

Ответ 3

Этот код немного переборчив, я думаю.

(из примера python https://docs.python.org/2/library/ftplib.html) После ftp.login() и установки ftp.cwd() вы можете просто использовать:

os.chdir(ddir)
ls = ftp.nlst()
count = len(ls)
curr = 0
print "found {} files".format(count)
for fn in ls:
    curr += 1
    print 'Processing file {} ... {} of {} ...'.format(fn, curr, count)
    ftp.retrbinary('RETR ' + fn, open(fn, 'wb').write)

ftp.quit()
print "download complete."

чтобы загрузить все файлы.

Ответ 4

Я новичок, поэтому я не сделал код эффективно, но я сделал это и протестировал его работу. Это то, что я сделал для загрузки файлов и папок с ftp-сайта, но с ограниченной глубиной в структуре файлов.

try:
   a = input("Enter hostname : ")
   b = input("Enter username : ")
   c = input("Enter password : ")
   from ftplib import FTP
   import os
   os.makedirs("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp")
   os.chdir("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp")
   ftp = FTP(host = a, user= b, passwd = c)
   D = ftp.nlst()
   for d in D:
      l = len(d)
      char = False
      for i in range(0,l):
          char = char or d[i]=="."
      if not char:
         ftp.cwd("..")
         ftp.cwd("..")
         E = ftp.nlst("%s"%(d))
         ftp.cwd("%s"%(d))
         try:
             os.makedirs("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp\\%s"%(d))
         except:
             print("you can debug if you try some more")
         finally:
             os.chdir("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp\\%s"%(d))
             for e in E:
                l1 = len(e)
                char1 = False
                for i in range(0,l1):
                   char1 = char1 or e[i]=="."
                if not char1:
                   ftp.cwd("..")
                   ftp.cwd("..")
                   F = ftp.nlst("%s/%s"%(d,e))
                   ftp.cwd("%s/%s"%(d,e))
                   try:
                       os.makedirs("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp\\%s\\%s"%(d,e))
                   except:
                       print("you can debug if you try some more")
                   finally:
                       os.chdir("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp\\%s\\%s"%(d,e))
                       for f in F:
                           if "." in f[2:]:
                               with open(f,'wb') as filef:
                                   ftp.retrbinary('RETR %s' %(f), filef.write)
                           elif not "." in f:
                               try:
                                  os.makedirs("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp\\%s\\%s\\%s"%(d,e,f))
                               except:
                                  print("you can debug if you try some more")
                elif "." in e[2:]:
                   os.chdir("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp\\%s"%(d))
                   ftp.cwd("..")
                   ftp.cwd("..")
                   ftp.cwd("..")
                   ftp.cwd("%s"%(d))
                   with open(e,'wb') as filee:
                      ftp.retrbinary('RETR %s' %(e), filee.write)
      elif "." in d[2:]:
          ftp.cwd("..")
          ftp.cwd("..")
          os.chdir("C:\\Users\\PREM\\Desktop\\pyftp download\\ftp")
          with open(d,'wb') as filed:
             ftp.retrbinary('RETR %s'%(d), filed.write)
   ftp.close()
   print("Your files has been successfully downloaded and saved. Bye")
except:
    print("try again you can do it")
finally:
    print("code ran")

Ответ 5

Рекурсивное решение (py 2.7):

import os, ftplib, shutil, operator

def cloneFTP((addr, user, passw), remote, local):
    try:
        ftp = ftplib.FTP(addr)
        ftp.login(user, passw)
        ftp.cwd(remote)
    except: 
        try: ftp.quit()
        except: pass
        print 'Invalid input ftp data!'
        return False
    try: shutil.rmtree(local)
    except: pass
    try: os.makedirs(local)
    except: pass
    dirs = []
    for filename in ftp.nlst():
        try:
            ftp.size(filename)
            ftp.retrbinary('RETR '+ filename, open(os.path.join(local, filename), 'wb').write)
        except:
            dirs.append(filename)
    ftp.quit()
    res = map(lambda d: cloneFTP((addr, user, passw), os.path.join(remote, d), os.path.join(local, d)), dirs)
    return reduce(operator.iand, res, True)

Ответ 6

Вместо того, чтобы использовать Python lib для ftp загружать каталог, мы можем вызвать dos script из программы python. В dos script мы будем использовать собственный протокол ftp, который может загрузить весь файл из папки с помощью mget *.*.

fetch.bat
ftp -s:fetch.txt

fetch.txt
open <ipaddress>
<userid>
<password>
bin (set the mnode to binary)
cd </desired directory>
mget *.*
bye

fetch.py
import os
os.system("fetch.bat")