美文网首页IT@程序员猿媛Python小程序
python——ftp服务器指定日期所有文件的更新下载

python——ftp服务器指定日期所有文件的更新下载

作者: 末一哟 | 来源:发表于2019-02-20 21:36 被阅读163次

    盼望着盼望着,终于迎来了一篇原创(老泪纵横)。


    也是任务需求,花了两天弄了一下,现在总算是满足要求了。可能容错性还不够,冗余度应该还行。但是写过了才有体会,是真的痛苦。里面的小弯弯逻辑是头疼。下面就来说一下实现的功能:

    大前提:在规定的文件格式和路径规则下

    实现:ftp服务器上指定日期下的所有文件的更新下载

    说明:
    1.大前提,就是我项目要求的文件存放格式,因为有这个才能写出一键自动化的程序,否则。。。应该是痴人说梦(恕我才疏学浅吧) 归类规则

    2.具体实现的功能由如下知识点拼凑:python库ftplib的使用;os库的使用(本地文件操作);针对归类规则思考的程序逻辑结构(这才是最难的,一点一点摸索出来的)等。
    3.这里的指定日期下可不止一处日期哦,比如传感器A下有2019年1月1日,传感器B也有这个日期,那么该模块的功能是同时更新A与B下的指定日期的文件。怎么样,是不是挺工程化的。


    下面贴代码:

    # -*- coding: utf-8 -*-
    """
    Created on Wed Feb 20 16:56:26 2019
    
    @author: Raul
    """
    import ftplib
    import os
    import datetime
    
    ##  创建地址文件 创建成功返回true 已存在返回false
    def mkdir_ifnotExist(path):
        path = path.strip()    # 删除地址首尾空格
        path = path.rstrip("\\")    # 保留\\之后的内容 以空格结尾
        isExist = os.path.exists(path)
        
        if not isExist :
            os.makedirs(path)
            print("%s did not exist.\nNow is created."%path)
            return True
        else :
            print("%s has exist!"%path)
            return False
        
    ##  ftp连接函数
    def ftpconnect(ftpserver,port,username,password):
        ftp = ftplib.FTP()
        try:
            ftp.connect(ftpserver,port)
        except:
            raise(IOError('FTP connect failed!'))
        
        try:
            ftp.login(username,password)
        except:
            raise(IOError("FTP login failed!"))
        
        else:
            print("********* ftp连接、登录成功!*********")
            # 中文乱码问题
            ftp.encoding = 'GB18030'
            return ftp
    
    ##  ftp文件下载函数
    def ftpdownload(ftp,local_path,ftp_path,filename,bufsize = 1024*10):
        # 进入下载路径
        ftp.cwd(ftp_path)
        print("成功进入目录:",ftp.pwd().encode('iso-8859-1').decode('gbk'),"\n下载文件:",filename)    # 将GB18030转换成UFT可以显示的格式
        save_path = local_path + ftp_path
        # 调用地址创建函数
        mkdir_ifnotExist(save_path)
        save_path = save_path + "/" + filename
        fp = open(save_path,'wb')
        #print("打开本地保存文件...")
        ftp.retrbinary('RETR %s' % filename, fp.write, bufsize)
        #print("写入文件成功!")
        fp.close()
    
    ##  ftp退出连接函数  
    def ftpquit(ftp):
        try:
            ftp.quit()
        except:
            raise(IOError("FTP quit failed!"))
        else:
            print("*********ftp已断开连接!*********")
    
    ##  拷贝ftp服务器指定日期所有文件函数
    def copy_new_file(ftp):
        nlst = ftp.nlst()
        for name in nlst:
            # 调用具体实现功能函数 该函数为自迭代函数
            find_assign_file(ftp,name,0)
            # 逻辑需要 服务器退出到根目录下
            while ftp.nlst() != nlst:
                ftp.cwd("..")
    
    ##  查找指定文件自迭代函数
    def find_assign_file(ftp,file_name,year_flag):
        # year_flag: 当当前路径为指定年份时为1,否则为0
        folder_flag = 1    # folder_flag  0:非文件夹 1:文件夹
        cmd_count = 0    # 目录进入层数记录
        global detectionfolder_list    # 每个测点上的节点目录    
        global tmp_file     # 存放每个日期文件下的文件名列表 [[node1],[node2],...,[nodeN]]
        global record       # tmp_file的索引 使用:tmp_file[recor]
        global date_flag    # 0:非指定日期;1:指定日期;2:为当前指定日期的检测节点文件夹;3:保存指定日期文件夹中的文件
        try:
            ftp.cwd(file_name)         #需要判断的元素
            ftp.cwd("..")              #如果能通过路劲打开必为文件夹,在此返回上一级
        # 不能通过路径打开必为文件,抓取其错误信息
        except ftplib.error_perm as fe:
            folder_flag = 0    # 发现文件txt         
            if date_flag == 2:    # 父文件为节点文件夹
                date_flag = 3     # 下载标志
        finally:
            if folder_flag == 1:    # 要访问的是文件夹
                father_file_list = ftp.nlst()    # 获取当前路径下所有文件夹
                # 逐个访问该路径下所有文件夹
                for father_name in father_file_list:
                    try:
                        ftp.cwd(father_name)
                        # 若当前路径下不为空
                        if ftp.nlst() != []:
                            # 逐个访问其子文件夹
                            for child_name in ftp.nlst():
                                # 获取节点文件名
                                try:
                                    ftp.cwd(child_name)
                                    cmd_count = 1 
                                    ftp.cwd(ftp.nlst()[0])
                                    cmd_count = 2
                                    ftp.cwd(ftp.nlst()[0])
                                    cmd_count = 3
                                    ftp.cwd(ftp.nlst()[0])
                                    cmd_count = 4
                                    # 操作正常返回到原路径
                                    ftp.cwd("..")
                                    ftp.cwd("..")
                                    ftp.cwd("..")
                                    ftp.cwd("..")
                                except (ftplib.error_perm,IndexError) as fe:
                                    if cmd_count == 3:
                                        ftp.cwd("..")
                                        detectionfolder_list = ftp.nlst()
                                        ftp.cwd("..")
                                        ftp.cwd("..")
                                    else:
                                        while cmd_count > 0 :
                                            ftp.cwd("..")
                                            cmd_count -= 1
                                    pass
                                # 当且仅当date_flag == 1(符合查找日期要求)且 父文件为节点文件夹(子文件夹则为输出文件) 进入输出文件名模块
                                if date_flag == 1 and (father_name in detectionfolder_list):
                                    date_flag = 2    # 查询成功
                                else:
                                    # 当前子文件不是节点文件夹时
                                    if (child_name not in detectionfolder_list):
                                        # 如果年份满足查询要求,令年份标志为1
                                        if (father_name == "2019年"): #"""or father_name == "1月" """): and (child_name == "1月" """or child_name == "1日" """)):
                                            year_flag = 1    # 年份标志为避免差错年份,比如查找2019年1月1日,如不添加其,则也会查找到2018年1月1日
                                        # 如果年份标志为1,且月、日也满足查询要求,此时令date_flag=1,即查询到指定日期
                                        if year_flag == 1 and father_name == "1月" and child_name=="1日":
                                            date_flag = 1
                                        else:
                                            date_flag = 0
                                    # 当前子文件是节点文件但父文件不符合查询日期
                                    elif father_name != "1日":
                                        date_flag = 0                      
                                # 进入子文件迭代查询
                                find_assign_file(ftp,child_name,year_flag)
                                # 查询完成后退回原路径
                                ftp.cwd("..")
                                # 只有当找到文件才会返回
                                # 一个节点文件夹进行一次查询即可(因为节点文件夹中都是txt)
                                break
                        # 子文件的路径下为空,返回到父路径
                        else:
                            ftp.cwd("..")
                    # 父路径为空,跳过
                    except ftplib.error_perm as er:
                        pass
            # 访问的是文件txt                                              
            else:
                if date_flag == 3:    # 3是符合读取要求的标志
                    # 令日期下文件夹数加1
                    tmp_file.append([])
                    for name in ftp.nlst():
                        # 如果当前路径下的文件不在上一时刻该文件夹下 则下载
                        if name not in tmp_file[record]:
                            t = ftp.pwd().encode('iso-8859-1').decode('gbk')
                            ftpdownload(ftp,save_path + t,"",name)
                            # 更新该文件夹下文件内容
                            tmp_file[record] += name
                    date_flag = 1    # 重置查找日期标志为1,检查下一节点文件夹
                    record += 1      # 日期文件计数+1,跟着程序进入下一符合查询日期的日期文件夹下,这相当于:tmp_file=[[第一个符合查询要求的文件夹],[第二个符合查询要求的文件夹],...,[record]]
                                     # 我们根据查询顺序默认相同的原理,记录一个随查找自增长的序号以索引符合要求的查找文件夹
    
    
    ## RUN
    # 固定参数信息
    ftpserver = '192.168.1.100'
    port = 2122
    username = 'raul'
    password = '123321'
    save_path = "C:/Users/Administrator/Desktop/桥梁备份数据"
    # 全局变量
    detectionfolder_list = []       # 每个测点上的节点目录   
    tmp_file = []                   # 存放每个日期文件下的文件名列表 [[node1],[node2],...,[nodeN]]
    date_flag = 0    # 0:非指定日期;1:指定日期;2:为当前指定日期的检测节点文件夹;3:保存指定日期文件夹中的文件
    record = 0                      # tmp_file的索引 使用:tmp_file[recor]
    # 运行结构
    # 1.连接ftp服务器
    ftp = ftpconnect(ftpserver,port,username,password)
    # 2.拷贝新文件 ps:这里还需改动,即将程序中的 年-月-日要求改为具体要求,即当时时间的日期datetime.datetime.now().year/month/day
    copy_new_file(ftp)
    # 3.断开ftp服务器连接
    ftpquit(ftp)
    

    函数各个模块写得还算清晰(无尽的写+一点点强迫症),注释也写的算完整的。需要注意的是这里我用于测试的,日期给的2019年1月1日,如果真的放到系统中实时更新,应调用datetime.datetime.now().year/month/day来替代。
    但需要说一下的是,这里的逻辑,懂的人可能能从我的程序里面看出来,也不是很特别复杂,但还是有些绕的。所以如果你也想自己尝试一下的话,最好先按照我的来跑一遍。跑通了,再自己飞~

    给两个效果图(gif9.62MB,加载慢直接看图或无视): 程序演示 对整个FTP服务器的监听 只更新指定日期下文件

    这里在说一下win7上ftp服务器的搭建,戳这里
    搭建需注意如下几点:

    • 请把FTP服务器和Web管理工具的所有子文件选项打钩(涂满深色表示未全选,不行滴) FTP服务器和Web管理工具全选√
    • 添加网址IP地址的时候,可下拉选择可用IP中当前电脑查询得到的IP

      (cmd下ipconfig的IPV4地址) 添加网站使用的IP
    • 添加FTP站点时的IP同上选择 添加FTP站点的IP 注意,端口最好改一下,虽然全网默认21.
      最后创建发布完成后,浏览器输入:
      ftp://192.168.1.100:2121+用户名和密码
      访问成功即创建成功!
      还有一个使用的注意点:访问成功后,如果您使用的局域网路由器网线,那么您的这个浏览器登录FTP服务器的方法,只能在你本机操作,如果想让别的电脑也通过浏览器访问的话,需要在192.168.1.1上对路由器的对外IP进行统一设置才行。否则外界读取到的IP并非你本机IP的矛盾会暴露出来的哦。但是通过命令行或程序,比如上文的python的ftplib库函数进行ftp服务器的登录连接,是没问题的。(可能是因为不涉及浏览器上NDS的问题)

    我们没能力发现知识,我们只是知识的寄生虫

    相关文章

      网友评论

        本文标题:python——ftp服务器指定日期所有文件的更新下载

        本文链接:https://www.haomeiwen.com/subject/ttekyqtx.html