不啰嗦,直接上代码:
import ftplib
import os
import socket
import sys
import timeHOST = '**' # ftp地址
USER = '**' # 用户名
PASSWD = '**' # 用户密码LocalDir = r'**' # 本地存贮路径
FTPDir = sys.argv[1] # 需要下载的ftp目录路径
local_fname = 'checkfile.txt' # 用本地存放已下载过的文件名local_files = [] # 存放从checkfile.txt中读回的文件名
appendFiles = [] # 存放需要写进checkfile.txt的文件各 def FtpConnect(host, username, passwd):
'''
连接并登录ftp服务器 host:ftp地址
username:用户名
passwd:用户密码
'''
try:
ftp = ftplib.FTP(HOST)
# ftp.encoding = 'utf-8' #解决中文乱码问题
# ftp.set_debuglevel(0) #不开启调试模式
except (socket.error, socket.gaierror):
print('Error, cannot reach ' + HOST)
return None
else:
print('Connect To Host Success...') try:
ftp.login(USER, PASSWD)
except ftplib.error_perm:
print('Username or Passwd Error')
# ftp.quit()
return None
else:
print(ftp.getwelcome()) # 显示登录ftp信息
print('Login Success...')
ftp.dir() # 显示目录下所有目录信息
print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
return ftp def filelist(ftp):
'''
递归ftp当前目录下的所有文件及目录信息
'''
flist = []
ftp.dir('.', flist.append) # 将目录中的内容存进flist列表
files = [f.split()[-1] for f in flist if f.startswith('-')] # 读取flist列表中的信息,以-开头的是常规文件,将该信息以空字符分割成列表,取最后的元素即为文件名
fids = [f.split(None, 4)[-1] for f in flist if f.startswith('-')] # 读取flist列表中的信息,以-开头的是常规文件,将该信息以前4个空字符分割成列表,
# 最后的元素包括了文件的大小,修改日期时间,可作为文件的标识
dictf = dict(zip(files, fids)) # 将文件名与对应的标识合成字典
dirs = [f.split()[-1] for f in flist if f.startswith('d')] # 读取flist列表中的信息,以d开头的是目录,将该信息以空字符分割成列表,取最后的元素即为目录名
# print(dirs)
return (dictf, dirs) def FtpDownloadDir(ftp, ftp_dir, local_dir):
'''
递归下载ftp指定目录下的所有文件及目录
'''
print('+++++++++++++++++++++++++++++++++++++++++++++++++++++')
print(f'Walking to {ftp_dir}')
print(f'Walking to {local_dir}')
if ftp_dir == "/":
dirname = "AllDir" # 用于本地创建新目录,如果下载的是FTP根目录,则在目录名为AllDir
else:
dirname = os.path.basename(ftp_dir) # 否则本地目录名与FTP目录名一样 ftp.cwd(ftp_dir) # 进入ftp对应目录
os.chdir(local_dir) # 进入本地下载目录 if os.path.exists(dirname): # 如果本地dirname目录已存在
os.chdir(dirname) # 则直接进入该目录
else:
try:
os.mkdir(dirname) # 否则在本地创建该目录
except OSError:
print('OSError!')
else:
os.chdir(dirname) # 创建完后进入该目录 ftp_curr_dir = ftp.pwd() # 获取FTP当前目录路径
print(ftp_curr_dir)
local_curr_dir = os.getcwd() # 获取本地当前目录路径
# print(f'Changing to {ftp_curr_dir}')
# print(f'Changing to {local_curr_dir}') dictf, dirs = filelist(ftp) # 调用filelist函数,递归ftp当前目录下的所有文件及目录
for f, k in dictf.items(): # 获取到的文件信息的键值对
if ".tif" not in f or ".tif" not in k:
continue
else:
if k not in local_files: # 文件标识与本地存储的已下载过的文件标识做对比
FtpDownloadFile(ftp, f, f) # k不在local_files中说明该文件未下载过,则下载该文件
appendFiles.append(k) # 同时将该文件的标识存储到appenFiles列表中,用于下载完成后更新本地的checkfile.txt文件 for d in dirs: # 对子目录进行处理
if isVaildDate(d):
if compare_time(d) + 24*3600*3 >= 0: #抓取最近三天的
FtpDownloadDir(ftp, d, local_curr_dir) # 调用自身,递归下载子目录中的文件
ftp.cwd('..')
os.chdir('..') # 每次递归完成后,ftp及本地都返回上一层目录,继续其他子目录的处理
else:
FtpDownloadDir(ftp, d, local_curr_dir) # 调用自身,递归下载子目录中的文件
ftp.cwd('..')
os.chdir('..') # 每次递归完成后,ftp及本地都返回上一层目录,继续其他子目录的处理
os.chdir(local_dir)def isVaildDate(date):
try:
if len(date) == 4:
time.strptime(date, "%Y")
return True
if len(date) == 6:
time.strptime(date, "%Y%m")
return True
if len(date) == 8:
time.strptime(date, "%Y%m%d")
return True
return False
except:
return False def compare_time(time1):
fmt="%Y"
if len(time1) == 6:
fmt="%Y%m"
if len(time1) == 8:
fmt="%Y%m%d"
time2=time.strftime(fmt,time.localtime(time.time()))
s_time = time.mktime(time.strptime(time1,fmt))
e_time = time.mktime(time.strptime(time2,fmt))
return int(s_time) - int(e_time)def FtpDownloadFile(ftp, remotefile, localfile):
'''
下载ftp当前目录的文件到本地的当前目录中
'''
buffer_size = 10240 # 默认是8192
try:
f = open(localfile, 'wb')
ftp.retrbinary(f'RETR {remotefile}', f.write, buffer_size)
except ftplib.error_perm:
print(f'File:{f} Download Error')
# os.unlink(localpath)
else:
print(f'File:{f} Download Success...')
finally:
f.close() def operfile(fileTxt, op):
'''
操作下载目录中的文件人工
op为'r'时读取该文件,如文件不存在则忽略
op为'w'时追加写入文件
'''
fp = os.path.join(LocalDir, fileTxt)
if op == 'r':
print(f'从 {fp} 中读取本地文件列表')
try:
with open(fp, 'r')as ft:
for line in ft:
line = line.strip()
local_files.append(line)
except Exception as e:
print(e)
elif op == 'w':
print(f'更新 {fileTxt} 中文件列表')
try:
with open(fp, 'a') as ft:
ft.writelines([f'{x}\n' for x in appendFiles])
except Exception as e:
print(e)
else:
print("参数2请输入'r'或者'w'!") if __name__ == '__main__':
config = sys.argv[1]
ftp = FtpConnect(HOST, USER, PASSWD) # 连接并登录ftp服务器
ftp.encoding = 'utf-8'
if ftp: # 如果登录成功
operfile(local_fname, 'r') # 从checkfile.txt中获取已下载过的文件
FtpDownloadDir(ftp, FTPDir, LocalDir) # 将ftp指定目录下的文件更新到本地目录中
if appendFiles: # 如果有新文件更新到本地
operfile(local_fname, 'w') # 则将其追加到checkfile.txt中
appendFiles = [] # 清空列表
else:
print(f'无需更新{local_fname}')
ftp.quit()
print("FTP QUIT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")