【问题标题】:Using Python's ftplib to get a directory listing, portably使用 Python 的 ftplib 获取目录列表,可移植
【发布时间】:2010-09-11 20:02:53
【问题描述】:

您可以使用 ftplib 在 Python 中获得完整的 FTP 支持。然而,获取目录列表的首选方式是:

# File: ftplib-example-1.py

import ftplib

ftp = ftplib.FTP("www.python.org")
ftp.login("anonymous", "ftplib-example-1")

data = []

ftp.dir(data.append)

ftp.quit()

for line in data:
    print "-", line

产量:

$ python ftplib-example-1.py
- total 34
- drwxrwxr-x  11 root     4127         512 Sep 14 14:18 .
- drwxrwxr-x  11 root     4127         512 Sep 14 14:18 ..
- drwxrwxr-x   2 root     4127         512 Sep 13 15:18 RCS
- lrwxrwxrwx   1 root     bin           11 Jun 29 14:34 README -> welcome.msg
- drwxr-xr-x   3 root     wheel        512 May 19  1998 bin
- drwxr-sr-x   3 root     1400         512 Jun  9  1997 dev
- drwxrwxr--   2 root     4127         512 Feb  8  1998 dup
- drwxr-xr-x   3 root     wheel        512 May 19  1998 etc
...

我想这个想法是解析结果以获取目录列表。然而,这个列表直接依赖于 FTP 服务器格式化列表的方式。必须预测 FTP 服务器可能会格式化此列表的所有不同方式,为此编写代码会非常麻烦。

是否有一种可移植的方式来获取一个包含目录列表的数组?

(数组应该只有文件夹名称。)

【问题讨论】:

    标签: python ftp portability


    【解决方案1】:

    尝试使用ftp.nlst(dir)

    但请注意,如果文件夹为空,则可能会抛出错误:

    files = []
    
    try:
        files = ftp.nlst()
    except ftplib.error_perm as resp:
        if str(resp) == "550 No files found":
            print "No files in this directory"
        else:
            raise
    
    for f in files:
        print f
    

    【讨论】:

    • 是的,这回答了这个问题,我认为这很好,但它完全不是一个可扩展的答案。如果我需要一个带有属性的文件对象怎么办?不要悲观,但它会很方便。
    • 谢谢。 5 年后:自 3.3 版起已弃用:改用 mlsd()。
    • 即使在今天,很多 ftp 服务器还不支持MLSD 命令。
    • 我认为 ftplib 进化了:我在 python 3.8 中,如果目录为空,我没有得到异常,而只是一个空列表 []
    【解决方案2】:

    LIST 响应的布局没有标准。您必须编写代码来处理最流行的布局。我将从 Linux ls 和 Windows Server DIR 格式开始。不过,那里有很多种类。

    如果您无法解析更长的列表,请回退到 nlst 方法(返回 NLST 命令的结果)。对于奖励积分,作弊:也许包含已知文件名的行中最长的数字是它的长度。

    【讨论】:

    • 永远不要假设。当你最不期望它们时,猜测总是会导致模糊的错误
    • 非常正确,因此我进行了许多单元测试和集成测试。 :) 但是,如果他们需要长度,那就是:希望格式与他们测试过的格式匹配;休息;或尝试找出在哪里可以找到长度。没有一个选项是理想的。
    【解决方案3】:

    解析 FTP 目录列表的可靠/标准化方法是使用 MLSD 命令,目前所有最新/体面的 FTP 服务器都应该支持该命令。

    import ftplib
    f = ftplib.FTP()
    f.connect("localhost")
    f.login()
    ls = []
    f.retrlines('MLSD', ls.append)
    for entry in ls:
        print entry
    

    上面的代码会打印出来:

    modify=20110723201710;perm=el;size=4096;type=dir;unique=807g4e5a5; tests
    modify=20111206092323;perm=el;size=4096;type=dir;unique=807g1008e0; .xchat2
    modify=20111022125631;perm=el;size=4096;type=dir;unique=807g10001a; .gconfd
    modify=20110808185618;perm=el;size=4096;type=dir;unique=807g160f9a; .skychart
    ...
    

    从 python 3.3 开始,ftplib 将提供一个特定的方法来做到这一点:

    【讨论】:

      【解决方案4】:

      我碰巧遇到了似乎不支持 MLSD 的 FTP 服务器(Rackspace Cloud Sites 虚拟服务器)。然而,我需要几个文件信息字段,例如大小和时间戳,而不仅仅是文件名,所以我必须使用 DIR 命令。在这台服务器上,DIR 的输出看起来非常像 OP 的。如果它对任何人有帮助,这里有一个 Python 小类,它解析一行这样的输出以获取文件名、大小和时间戳。

      导入日期时间

      class FtpDir:
          def parse_dir_line(self, line):
              words = line.split()
              self.filename = words[8]
              self.size = int(words[4])
              t = words[7].split(':')
              ts = words[5] + '-' + words[6] + '-' + datetime.datetime.now().strftime('%Y') + ' ' + t[0] + ':' + t[1]
              self.timestamp = datetime.datetime.strptime(ts, '%b-%d-%Y %H:%M')
      

      我知道,不是很便携,但很容易扩展或修改以处理各种不同的 FTP 服务器。

      【讨论】:

        【解决方案5】:

        这是来自 Python 文档

        >>> from ftplib import FTP_TLS
        >>> ftps = FTP_TLS('ftp.python.org')
        >>> ftps.login()           # login anonymously before securing control 
        channel
        >>> ftps.prot_p()          # switch to secure data connection
        >>> ftps.retrlines('LIST') # list directory content securely
        total 9
        drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 .
        drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 ..
        drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 bin
        drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 etc
        d-wxrwxr-x   2 ftp      wheel        1024 Sep  5 13:43 incoming
        drwxr-xr-x   2 root     wheel        1024 Nov 17  1993 lib
        drwxr-xr-x   6 1094     wheel        1024 Sep 13 19:07 pub
        drwxr-xr-x   3 root     wheel        1024 Jan  3  1994 usr
        -rw-r--r--   1 root     root          312 Aug  1  1994 welcome.msg
        

        【讨论】:

          【解决方案6】:

          这对我的代码有帮助。

          当我尝试仅对一种类型的文件进行毡合并通过添加在每一行上进行测试的条件将它们显示在屏幕上时。

          这样

          elif command == 'ls':
              print("directory of ", ftp.pwd())
              data = []
              ftp.dir(data.append)
          
              for line in data:
                  x = line.split(".")
                  formats=["gz", "zip", "rar", "tar", "bz2", "xz"]
                  if x[-1] in formats:
                      print ("-", line)
          

          【讨论】:

            【解决方案7】:

            我在尝试获取文件名、最后修改的标记、文件大小等时找到了我的方式,并想添加我的代码。只花了几分钟就编写了一个循环来解析ftp.dir(dir_list.append),利用python std lib 的东西,比如strip()(清理文本行)和split() 创建一个数组。

            ftp = FTP('sick.domain.bro')
            ftp.login()
            ftp.cwd('path/to/data')
            
            dir_list = []
            ftp.dir(dir_list.append)
            
            # main thing is identifing which char marks start of good stuff
            # '-rw-r--r--   1 ppsrt    ppsrt      545498 Jul 23 12:07 FILENAME.FOO
            #                               ^  (that is line[29])
            
            for line in dir_list:
               print line[29:].strip().split(' ') # got yerself an array there bud!
               # EX ['545498', 'Jul', '23', '12:07', 'FILENAME.FOO']
            

            【讨论】:

            • 我喜欢这个。很干净。我刚刚使用了“打印线”,对我来说已经足够了。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-25
            • 2011-03-14
            • 1970-01-01
            • 2021-10-01
            • 2013-06-27
            • 2011-09-05
            相关资源
            最近更新 更多