Python 遍历FTP列表

Python 遍历FTP列表,python,ftp,traversal,Python,Ftp,Traversal,我试图从FTP服务器获取所有目录的名称,并将它们以层次顺序存储在多维列表或dict中 例如,包含以下结构的服务器: /www/ mysite.com images png jpg 在脚本的末尾,将给我一个列表,如 ['/www/' ['mysite.com' ['images' ['png'], ['jpg'] ] ] ] 我尝试过使用如下递归函数: def遍历(dir):

我试图从FTP服务器获取所有目录的名称,并将它们以层次顺序存储在多维列表或dict中

例如,包含以下结构的服务器:

/www/
    mysite.com
        images
            png
            jpg
在脚本的末尾,将给我一个列表,如

['/www/'
  ['mysite.com'
    ['images'
      ['png'],
      ['jpg']
    ]
  ]
]
我尝试过使用如下递归函数: def遍历(dir): FTP.dir(dir,transverse)

FTP.dir以以下格式返回行:

drwxr-xr-x    5 leavesc1 leavesc1     4096 Nov 29 20:52 mysite.com
这样,第[56:]行只会给我一个目录名(mysite.com)。我在递归函数中使用它

但是我不能让它工作。我尝试过许多不同的方法,但都无法成功。还有很多FTP错误(或者找不到目录,这是一个逻辑问题,有时服务器返回意外错误,没有日志,我无法调试)

底线问题:
如何从FTP服务器获取分层目录列表?

这里是一个简单而缓慢的实现。它的速度很慢,因为它试图对每个目录条目进行CWD,以确定它是目录还是文件,但这是可行的。可以通过解析LIST命令输出对其进行优化,但这在很大程度上依赖于服务器实现

import ftplib

def traverse(ftp, depth=0):
    """
    return a recursive listing of an ftp server contents (starting
    from the current directory)

    listing is returned as a recursive dictionary, where each key
    contains a contents of the subdirectory or None if it corresponds
    to a file.

    @param ftp: ftplib.FTP object
    """
    if depth > 10:
        return ['depth > 10']
    level = {}
    for entry in (path for path in ftp.nlst() if path not in ('.', '..')):
        try:
            ftp.cwd(entry)
            level[entry] = traverse(ftp, depth+1)
            ftp.cwd('..')
        except ftplib.error_perm:
            level[entry] = None
    return level

def main():
    ftp = ftplib.FTP("localhost")
    ftp.connect()
    ftp.login()
    ftp.set_pasv(True)

    print traverse(ftp)

if __name__ == '__main__':
    main()

您不会喜欢这样,但是“它取决于服务器”,或者更准确地说,“它取决于服务器的输出格式”

可以将不同的服务器设置为显示不同的输出,因此在一般情况下,您的初始方案注定会失败


上面的“幼稚而缓慢的实现”将导致足够多的错误,一些FTP服务器将切断您的连接(这可能是其中7个服务器之后发生的情况…。

如果我们使用Python,请查看:

(操作系统路径行走)


如果已经有了一个很好的模块,不要重新发明轮子。真不敢相信上面的帖子有两个ups,无论如何,请欣赏。

如果服务器支持
MLSD
命令,那么使用answer中的“a目录及其子体”代码。

这里是一个对我有用的Python 3脚本的初稿。它比调用
cwd()
快得多。作为参数传入服务器、端口、目录、用户名和密码。我将输出作为列表留给读者作为练习

import ftplib
import sys

def ftp_walk(ftp, dir):
    dirs = []
    nondirs = []
    for item in ftp.mlsd(dir):
        if item[1]['type'] == 'dir':
            dirs.append(item[0])
        else:
            nondirs.append(item[0])
    if nondirs:
        print()
        print('{}:'.format(dir))
        print('\n'.join(sorted(nondirs)))
    else:
        # print(dir, 'is empty')
        pass
    for subdir in sorted(dirs):
        ftp_walk(ftp, '{}/{}'.format(dir, subdir))

ftp = ftplib.FTP()
ftp.connect(sys.argv[1], int(sys.argv[2]))
ftp.login(sys.argv[4], sys.argv[5])
ftp_walk(ftp, sys.argv[3])

你的问题是什么?替代技术?调试策略?如何在服务器上启用日志记录?“还有别的吗?”马丁·卡彭特-如果我不太清楚的话,对不起。我刚刚编辑了这篇文章,陈述了实际的问题。非常感谢你的回复!但愿我能接受它5倍!:)我刚刚意识到,虽然这是可行的,但并不是一直都有效。我的根目录中有18个目录。该脚本成功地遍历了其中的7个。其余部分只是没有添加到级别目录中。您可以在
drwxr-xr-x
中检查行的第一个字符-
d
,表示它是一个目录。如果不是,就跳过它。另一种方法是解析列表的输出-R@LiraNuna:事实上你不能。第一个字符也可以是l,表示这是指向文件或目录的符号链接。一般来说,FTP协议的定义强烈依赖于服务器。使用MLSD怎么样?上面写着“type=dir”或“type=file”。但是,我找不到使用ftp.retrlines()代替ftp.nlst()的方法(不管第一个参数是什么)。主要是因为ftp.nlst()会返回元素列表,因为ftp.retrlines()会打印到sys.stdout。我曾尝试将输出传递给一个函数以存储在全局列表中,并在遍历函数中循环遍历该列表,即:“for entry in list:etc”,但它不起作用。如果有人愿意帮忙,我们将不胜感激:)再次感谢你们所有人@莱拉:看看我在一个相关问题中的答案,我使用了一个类方法作为回调。你不能在FTP服务器上这样做。很可能。那么,是否有另一种方法可以获取站点的目录结构?