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()
Могу поспорить, что вы можете сказать, что он мало что делает, когда я запускаю его, поэтому любые предложения по улучшению будут с благодарностью.
Ответы
Ответ 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")