使用Python ftplib下载时忽略丢失的文件

时间:2015-02-07 19:00:04

标签: python ftp ftplib try-except

我正在尝试从FTP服务器下载某个文件(名为010010-99999-year.gz)。同一个文件,但不同年份存在于不同的FTP目录中。例如:

ftp://ftp.ncdc.noaa.gov/pub/data/noaa/isd-lite/2000/010010-99999-1973.gz ftp://ftp.ncdc.noaa.gov/pub/data/noaa/isd-lite/2001/010010-99999-1974.gz 等等。图片说明了其中一个目录: enter image description here

该文件不在所有目录中(即所有年份)。在这种情况下,我希望脚本忽略丢失的文件,打印“不可用”,并继续下一个目录(即明年)。 我可以使用NLST列表,首先生成当前FTP目录中的文件列表,然后检查我的文件是否在该列表上,但这很慢,NOAA(拥有服务器的组织)不喜欢文件列表(source)。因此我提出了这个代码:

def FtpDownloader2(url="ftp.ncdc.noaa.gov"):
    ftp=FTP(url)        
    ftp.login()
    for year in range(1901,2015):
        ftp.cwd("/pub/data/noaa/isd-lite")
        ftp.cwd(str(year))
        fullStationId="010010-99999-%s.gz" % year
        try:              
            file=open(fullStationId,"wb")
            ftp.retrbinary('RETR %s' % fullStationId, file.write)
            print("File is available")
            file.close()
        except: 
            print("File not available")
    ftp.close()

这会正确下载现有文件(1973 - 2014年),但它也会在1901 - 1972年间生成空文件。该文件不在1901-1972的FTP中。 我在使用try和其他问题时做错了什么,还是其他问题?

2 个答案:

答案 0 :(得分:2)

我拿了你的代码并对其进行了一些修改:

from ftplib import FTP, error_perm
import os

def FtpDownloader2(url="ftp.ncdc.noaa.gov"):
    ftp = FTP(url)
    ftp.login()
    for year in range(1901, 2015):
        remote_file = '/pub/data/noaa/isd-lite/{0}/010010-99999-{0}.gz'.format(year)
        local_file = os.path.basename(remote_file)
        try:
            with open(local_file, "wb") as file_handle:
                ftp.retrbinary('RETR %s' % remote_file, file_handle.write)
            print('OK', local_file)
        except error_perm:
            print('ERR', local_file)
            os.unlink(local_file)
    ftp.close()

注释

  • 一个人可以做的最危险的操作是拥有一个没有特定异常类的except子句。这种类型的构造将忽略所有错误,使其难以排除故障。为解决此问题,我添加了特定的例外error_perm
  • 一旦发生异常,我绝对知道本地文件已关闭,因为with语句保证
  • 如果发生error_perm异常,我删除了本地文件,表明该文件无法从服务器获取
  • 我删除了代码以更改目录:对于每年,您cwd两次会导致进程变慢
  • range(1901, 2015)将不包括2015年。如果您需要,则必须指定range(1901, 2016)
  • 我改进了print语句以包含文件名,从而更容易跟踪哪些可用,哪些不可用

更新

此更新回答了有关不创建空本地文件(然后必须删除它们)的问题。有几种不同的方式:

  1. 下载前查询远程文件的存在。仅在远程存在时创建本地文件。这种方法的问题是查询远程文件比创建/删除本地文件花费的时间更长。
  2. 创建一个字符串缓冲区(StringIO),下载到该缓冲区。仅当该字符串缓冲区不为空时才创建本地文件。这种方法的问题是你要写两次相同的数据:一次到字符串缓冲区,一次从字符串缓冲区到文件。

答案 1 :(得分:1)

我认为问题出在你的尝试中:除了块,你在检查文件是否存在之前保持文件处理程序为新文件打开:

try:              
    file=open(fullStationId,"wb")
    ftp.retrbinary('RETR %s' % fullStationId, file.write)
    print("File is available")
    file.close()
except: 
    print("File not available")

相反,在except块中添加一个额外的语句来关闭文件处理程序,另一个语句删除文件,如果它是空的。

另一种可能性是,只有当文件存在并且使用ftp.size在服务器上具有非零大小时才打开本地写入文件