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()
我敢打赌,你们能看出来我运行这个脚本时,它的功能很有限,所以如果你们有改进的建议,我会非常感激。
6 个回答
我觉得这段代码有点复杂了。
(来自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."
就可以下载所有文件了。
如果你只是想解决一个问题,我建议你可以试试 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 更好用。
编辑:你需要给 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
成员中查看详细信息。
我已经搞定了这个问题,所以现在把相关的代码分享出来,方便以后来的人参考:
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上是有效的。