5G协议批量下载 python3_批量下载3gpp 协议-程序员宅基地

技术标签: python3  5G  python  5g  

最近需要3GPP的5G协议,手动一个一个下载比较麻烦,抽空用python3写了个下载程序,可以从3gpp的ftp服务器批量下载协议文件。

3GPP协议下载地址:https://www.3gpp.org/ftp/Specs/archive/38_series/

代码使用了线程池,可以多线程从3gpp下载协议。

代码流程:

1、首先从下载地址获取要下载的所有文件的 文件名、url、大小、日期、本地存储地址,写入ini文件

2、读取ini文件中的信息,使用线程池进行文件下载

#coding:utf-8
import urllib
import urllib.request as urllib2
from bs4 import BeautifulSoup 
import socket
import os, sys
import time
import configparser
import csv
import codecs
import threading
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor  # 线程池,进程池
import signal
from configparser import ConfigParser


stop = False


class Series38DownloadClass():
    def __init__(self, downloadMode=0):
        # 定义锁和线程池
        # 实例化线程锁
        self.threadLock = threading.Lock()
        self.ini_lock = threading.Lock()
        self.maxThreadNum = 100
        # 定义2个线程的线程池
        self.thread_pool = ThreadPoolExecutor(self.maxThreadNum)
        # 当前任务列表
        self.task_handler_list = []
        # 定义2个进程的进程池。进程池的用法和线程池基本一样
        # self.process_pool = ProcessPoolExecutor(2)
        #所有的下载文件信息存放列表 
        self.downloadFileList = []
        #下载成功数
        self.downloadSuccessNum = 0
        #下载失败数
        self.downloadFailedNum = 0
        #需要下载的总数
        self.downloadFileNum = 0 
        self.downloadCount = 0;
        #3GPP协议文件信息
        self.seriesFile_ini = "seriesFile.ini"
        

    def main_logic(self, downloadMode):
        # 从网站获取所需下载文件的url
        if downloadMode == 0:
            self.get_series38()

        # 从文件获取所需下载文件的url
        if downloadMode == 1:
            self.readSeriesFile_ini()
            while len(self.downloadFileList) > 0 and (False == stop):
                fileInfo = self.downloadFileList.pop(0)
                # 添加的中间函数调用
                self.call_downloadFile(fileInfo)

        
        

    # 添加一个线程池调用get_sub_series的中间方法
    def call_updateFileInfo(self, subPath, subSeriesUrl):
        self.thread_pool.submit(self.get_sub_series, subPath, subSeriesUrl)

    # 添加一个线程池调用call_downloadFile的中间方法
    def call_downloadFile(self, fileInfo):
        task_handler = self.thread_pool.submit(self.downloadFile, fileInfo)
        self.task_handler_list.append(task_handler)
        while True:
            if True == stop:
                break
            for task_handler_tmp in self.task_handler_list:
                if task_handler_tmp.done():
                    self.task_handler_list.remove(task_handler_tmp)
            # 如果未完成的任务已多于线程数的两倍那么先停一下,先不要再增加任务,防止内存消耗
            if len(self.task_handler_list) > self.maxThreadNum * 2:
                time.sleep(2)
            else:
                return True

    def downloadFile(self,fileInfo):
        '''
        下载文件
        '''
        self.downloadCount += 1
        
        fileName = fileInfo[0]
        fileUrl = fileInfo[1]
        fileSize = fileInfo[2]
        fileDate = fileInfo[3]
        filePath = fileInfo[4]
        tmpDir = filePath.split('/')[0]
        if not os.path.exists(tmpDir):
            os.mkdir(tmpDir)
        if os.path.exists(filePath):
            localFileSize = os.path.getsize(filePath)
            size = str(1.0*localFileSize/1024)
            if size[size.index('.')+1] == '0':
                size = size[:size.index('.')]
                localFileSize = "%s KB"%(size)
            else:
                localFileSize = "%s KB"%(size[:size.index('.')+1+1])
                
            if localFileSize != fileSize:
                os.remove(filePath)
                try:
                    urllib2.urlretrieve(fileUrl, filePath)
                    print("重新下载成功:%s [%d/%d], 原大小 %s, 协议大小 %s"%
                        (fileName, self.downloadSuccessNum, self.downloadFileNum, localFileSize, fileSize))
                    self.downloadSuccessNum += 1
                except:
                    self.downloadFaildNum += 1
                    self.downloadFileList.append(fileInfo)
                    print("重新下载失败:%s [失败次数 %s]"%(fileName,self.downloadFaildNum ))
                time.sleep(1)
            else:
                self.downloadSuccessNum += 1
        else:
            try:
                urllib2.urlretrieve(fileUrl, filePath)
                print("首次下载成功:%s [%d/%d]"%(fileName, self.downloadSuccessNum, self.downloadFileNum))
                self.downloadSuccessNum += 1
            except:
                self.downloadFaildNum += 1
                self.downloadFileList.append(fileInfo)
                print("重新下载失败:%s [失败次数 %s]"%(fileName,self.downloadFaildNum ))
            time.sleep(1)

    def get_series38(self):
        ''' 
        从https://www.3gpp.org/ftp/Specs/archive/38_series/下载协议
        '''
        series38_url = 'https://www.3gpp.org/ftp/Specs/archive/38_series/'
        request = urllib2.Request(series38_url)
        # 向指定的url地址发送请求,并返回服务器响应的类文件对象
        response = urllib2.urlopen(request)
        # 服务器返回的类文件对象支持python文件对象的操作方法
        html = response.read()
        soup = BeautifulSoup(html.decode('utf-8'), "lxml")
        subSeriesList = soup.findAll('a')
        threadId = 0
        for line in subSeriesList:
            subUrl = line.get('href')
            if subUrl.startswith(series38_url):
                subPath = line.string
                self.get_sub_series(subPath, subUrl)
                self.call_updateFileInfo(subPath, subUrl);
                #增加sleep,防止服务器主动关闭连接
                time.sleep(2)

    def get_sub_series(self, subPath='', subUrl=''):
        '''
        获取子页面的协议文件信息
        '''
        fileInfoList = []
        request = urllib2.Request(subUrl)
        response = urllib2.urlopen(request)
        html = response.read()
        soup = BeautifulSoup(html.decode('utf-8'), "lxml")
        
        tbody = soup.find('tbody')
        file_tr = tbody.tr
        while file_tr and (False == stop):
            file_td = file_tr.td
            file_td = file_td.find_next_sibling()
            fileUrl= file_td.a.get('href')
            fileName = file_td.a.string.strip()
            file_td = file_td.find_next_sibling()
            fileDate = file_td.string.strip()
            file_td = file_td.find_next_sibling()
            fileSize = file_td.string.strip().replace(',','.')
            filePath = subPath+"/"+fileName
            fileInfoList.append([fileName,fileUrl,fileSize,fileDate,filePath])
            self.downloadFileList.append([fileName,fileUrl,fileSize,fileDate,filePath])
            file_tr = file_tr.find_next_sibling()
        #文件信息从web获取完毕,将信息写入ini文件
        self.ini_lock.acquire()
        conf = ConfigParser()
        conf.read(self.seriesFile_ini, encoding='utf-8')
        for fileInfo in fileInfoList:
            fileName = fileInfo[0]
            fileUrl = fileInfo[1]
            fileSize = fileInfo[2]
            fileDate = fileInfo[3]
            filePath = fileInfo[4]
            print(fileInfo)
            if not conf.has_section(fileName):
                conf.add_section(fileName)
            conf.set(fileName, "fileName", fileName)
            conf.set(fileName, "fileUrl", fileUrl)
            conf.set(fileName, "fileSize", fileSize)
            conf.set(fileName, "fileDate", fileDate)
            conf.set(fileName, "filePath", filePath)
        conf.write(open(self.seriesFile_ini, "w"))
        self.ini_lock.release()

    def readSeriesFile_ini(self):
        '''
        读取ini文件中的信息,并根据url信息使用线程池下载文件,
        '''
        if os.path.exists(self.seriesFile_ini):
            conf = ConfigParser()
            conf.read(self.seriesFile_ini, encoding='utf-8')
            for fileName in conf.sections():
                if True == stop:
                    break
                self.downloadFileNum += 1
                fileUrl = conf.get(fileName, "fileUrl")
                fileSize = conf.get(fileName, "fileSize")
                fileDate = conf.get(fileName, "fileDate")
                filePath = conf.get(fileName, "filePath")
                self.downloadFileList.append([fileName,fileUrl,fileSize,fileDate,filePath])
            print("ini文件中的文件总数 %d:"%(self.downloadFileNum))
# 自定义信号处理函数
def quit(signum, frame):
    stop = True
    print("-----------------------------------------------------------------------------")
    print("-------------------------------进程被终止-------------------------------------")
    print("-----------------------------------------------------------------------------")
    sys.exit()


if __name__ == "__main__":
    signal.signal(signal.SIGINT, quit)
    signal.signal(signal.SIGTERM, quit)
    #设置超时时间
    socket.setdefaulttimeout(60)

    obj = Series38DownloadClass()
    #下载地址读取协议的详细信息,并将读取信息写入ini文件
    obj.main_logic(0)

    #读取ini文件,并下载协议
    obj.main_logic(1)

    while True:
        for task_handler_tmp in obj.task_handler_list:
            if task_handler_tmp.done():
                obj.task_handler_list.remove(task_handler_tmp)
        if(len(obj.task_handler_list) == 0):
            break
    print("文件下载完毕,需要下载总数 %d, 已经下载总数 %d"%(obj.downloadFileNum, obj.downloadSuccessNum))
    print("退出主线程")

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/heima9000/article/details/109351085

智能推荐

大数据可视化设计师丹尼斯_自助数据可视化设计师如何谋生-程序员宅基地

文章浏览阅读1.5k次。大数据可视化设计师丹尼斯 重点 (Top highlight)I hit a point in my life where I could take my career in countless directions. I left my job in the summer of 2019 and began my venture as a freelancer. I work part-time..._hit a point

iOS 富文本之Label前后加文字“标签”_ios 字符串最后跟标签-程序员宅基地

文章浏览阅读2.1k次。先看需求图吧解析一下,title的文字换行,title前面加标签,这样的情况一般就会想到用NSMutableAttributedString富文本来解决,富文本的图文混排可以完美解决这个问题。但是,后台帅哥哥告诉本宝宝了,title前面的标签给返回的是文字,并不是图片……GG了……富文本可以指定部分文字的背景颜色,但是要求的这个标签有背景,也有圆角,没办法解决了。只有View..._ios 字符串最后跟标签

centos6.4.yum-lamp环境设置-程序员宅基地

文章浏览阅读47次。  首先防火墙开启mysql:3306 apache 80 and 81端口:vi /etc/sysconfig/iptables-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT #允许80端口通过防火墙-A INPUT -m state --state NEW -m tcp -p tcp --dpor...

kvm虚拟化学习笔记(十七)之KVM到KVM之v2v迁移-程序员宅基地

文章浏览阅读80次。原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处、作者信息和本声明。否则将追究法律责任。http://koumm.blog.51cto.com/703525/1304271KVM虚拟化学习笔记系列文章列表----------------------------------------kvm虚拟化学习笔记(一)之kvm虚拟化环境安..._virt-v2v -i vmx -it ssh -ip passwordfile

消息队列MQ的特点及API_mq与api接口的区别-程序员宅基地

文章浏览阅读4.1k次。消息队列技术是分布式应用间交换信息的一种技术。消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读走。通过消息队列,应用程序可独立地执行--它们不需要知道彼此的位置、或在继续执行前不需要等待接收程序接收此消息。消息队列的特点:异步 --- 消息队列本身是异步的,它允许接收者在消息发送很长时间后再取回消息,这和大多数通信协议是不同的。 解耦 --- 消息队列减少了..._mq与api接口的区别

王者荣耀服务器维护多久12.4,王者荣耀12月4日维护新活动介绍 王者荣耀维护到几点...-程序员宅基地

文章浏览阅读119次。王者荣耀今天维护到几点?王者荣耀12月4号要维护多久?王者荣耀维护到几点2018?应该有不少玩家在早上都发现了王者荣耀处于维护状态。那么具体的时间是怎么样的呢?下面就随琵琶网小编来了解一下!王者荣耀维护到几点?亲爱的召唤师:我们计划在2018年12月4日7:30-9:00对IOS手Q区、安卓微信区和IOS微信区进行不停机更新。更新结束后,登录即可领取钻石*120+铭文碎片*120!【更新时间】12...

随便推点

MVC3学习随记一-程序员宅基地

文章浏览阅读90次。最近才接触mvc,也是才接触linq语法,还有EntiyFramework,个人感觉这种开发模式还是挺不错的,随手记点笔记,简单做个增删改查吧一、实例化上下文ObjectContext:引用空间那些就跳过了。。db_testEntities test = new db_testEntities();二、用linq语法从数据库查询数据:使用linq语句查询返回的..._testentities的引用空间

耗时3个月,近10W字的刷题笔记,多语言通吃,秒杀 80% Leetcode 题目!_谷歌大佬算法刷题笔记(c++ 语言):-程序员宅基地

文章浏览阅读1w次,点赞88次,收藏360次。学会了,offer收割机就是你~_谷歌大佬算法刷题笔记(c++ 语言):

达梦数据库的约束操作_达梦数据库违反列id非空约束-程序员宅基地

文章浏览阅读6.4k次。达梦数据库的约束操作达梦数据库的约束常用的有主键约束、外键约束、唯一约束,还有些不常用的检查约束、非空约束等等。1、创建主键约束我们先来用test1用户创建一个tab1表来测试,同时创建idcreate table tab1 (id int primary key, name char);达梦数据库的约束操作插入一条数据正常insert into tab1 values(2, ‘a’);再次插入就会提示插入失败,提示违反表[TAB1]唯一性约束.insert into tab1 val_达梦数据库违反列id非空约束

2020年卫星行业研究报告_2020年卫星行业研究报告 csdn-程序员宅基地

文章浏览阅读3.1w次。核心观点:1、为什么这个时间点关注卫星通信领域?1)卫星发射模式及制造模式发生深刻变化:在SpaceX等公司带动下,火箭、卫星等制造模式从传统的定制化变为批量化,传统发射 制造成本大大降低,通信卫星可以低成本快速组网,卫星通信技术设施快速完成全球覆盖。2)卫星通信高通量趋势明显,带宽成本下降,降低用户门槛,卫星通信相关应用普及会加快:2022年,大容量(高通量)卫星将占 全球总卫星带宽的50%;2022年前后,我国宽带卫星容量需求也将达到1TB以上,带宽成本大大降低有利于带动应用普及。3)._2020年卫星行业研究报告 csdn

linux 组播接收和发送代码和组播问题总结_linux ip_add_membership 返回-1-程序员宅基地

文章浏览阅读8.3k次,点赞3次,收藏24次。linux 组播接收和发送代码和组播问题总结int main(int argc, char *argv[]){int sockfd;struct sockaddr_in localSock;struct sockaddr_in addr;struct ip_mreq group;int datalen;char databuf[4000];int r_linux ip_add_membership 返回-1

接口自动化测试实践指导(中):接口测试场景有哪些_接口测试的应用场景-程序员宅基地

文章浏览阅读1.8w次,点赞111次,收藏171次。在第一篇文章中详细给小伙伴们讲解了一下接口自动化需要做哪些准备工作,准备工作中最后一步接口测试用例设计是非常重要的一个环节,用例设计的好不好,直接关系到我们的测试质量,那如何进行测试用例设计呢,这里呢我结合自身经验,帮助大家梳理一下接口测试用例设计思路,希望对大家后续接口测试工作有所帮助和提升。......_接口测试的应用场景

推荐文章

热门文章

相关标签