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()

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


person Sosti    schedule 08.03.2011    source источник


Ответы (6)


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

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.

person Sosti    schedule 15.03.2011
comment
Рекомендуемый способ - использовать: ftp.quit() вместо ftp.close(). перейдите по этой ссылке - person Oran; 06.07.2014
comment
Как ftp.nlst () узнает, какая ссылка мне нужна? Этот ответ кажется неполным. - person Sören; 04.02.2021

Если это просто проблема, которую вы хотите решить, я могу предложить команду 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, например, представляют их пользователю в GUI, вероятно, должен иметь огромные груды специального кода для странных или малоизвестных серверов. И все они, вероятно, делают что-то глупое, сталкиваясь с вредоносными именами файлов.)

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

Изменить: вам нужно передать "вызываемый" объект в 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 объекта.

person sarnold    schedule 08.03.2011
comment
@Sosti, упоминание функции retrlines в моем посте - это гиперссылка на документацию :) - person sarnold; 08.03.2011
comment
Большое спасибо за это, все они звучат как солидные предложения! Я забыл упомянуть, что я использую Python 2.5 в Windows XP (если это вообще полезно). Если я использую опцию MLSD, ftp.retrlines ('MLSD') ', будет ли код работать для итерации или мне нужно изменить более? (конечно, это звучит немного глупо, но здесь новичок, помните?: DD) - person Sosti; 08.03.2011
comment
@Sosti, вам все равно придется изменить свой код: вы не можете использовать функцию os.walk(). Я немного отредактирую свой ответ, чтобы показать, как создать объект обратного вызова для retrlines. - person sarnold; 08.03.2011
comment
Я должен признать, что мне нужно провести некоторое исследование по этому поводу и попытаться написать несколько строк кода. Я надеялся, что проблема может быть решена путем настройки некоторых строк, но, видимо, проблема более фундаментальная. Сделаю все возможное, а затем вернусь с любыми результатами. Спасибо за ваш вклад и предложения! - person Sosti; 08.03.2011

этот код, я думаю, немного излишне.

(из примера 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."

чтобы скачать все файлы.

person kztd    schedule 24.01.2017

Рекурсивное решение (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)
person Iulian Bute    schedule 12.03.2019

Я новичок, поэтому я не сделал код эффективно, но я сделал и проверил, что он работает. Это то, что я сделал для загрузки файлов и папок с 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")
person PremVijay    schedule 20.03.2016
comment
Не могли бы вы объяснить, как / почему работает ваш код? Это позволит OP и другим понять и применить ваши методы (где это применимо) в другом месте. Ответы, содержащие только код, не приветствуются и могут быть удалены. - Во время проверки - person Wai Ha Lee; 20.03.2016

Вместо использования Python lib для загрузки каталога по ftp мы можем вызвать сценарий dos из программы python. В сценарии dos мы будем использовать собственный протокол 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")
person pythonadmirer    schedule 27.09.2012
comment
это также характерно для окон (dos) - person Carl; 12.08.2016
comment
Иногда это помогает. - person JOHN; 09.01.2018