从 GitHub 批量下载项目各版本的方法

一、脚本功能概述

这个 Python 脚本的主要功能是从 GitHub 上下载指定项目的各个发布版本的压缩包(.zip 和 .tar.gz 格式)。用户需要提供两个参数:一个是包含项目信息的 CSV 文件,另一个是用于保存下载版本信息的 CSV 文件。脚本会遍历项目列表,访问每个项目的 tags 页面,下载所有可用的版本压缩包,并记录相关信息到指定的 CSV 文件中。

二、脚本使用说明

在运行脚本前,请确保你已经安装了 requests 和 beautifulsoup4 库。如果未安装,可以使用以下命令进行安装:

bash

pip install requests beautifulsoup4

运行脚本时,在命令行中输入以下格式的命令:

bash

python script.py project_list.csv save_info.csv

其中,script.py 是脚本文件名,project_list.csv 是包含项目信息的 CSV 文件,save_info.csv 是用于保存下载版本信息的 CSV 文件。

下面是完整的脚本。

import requests
from bs4 import BeautifulSoup
import os
import csv
import sys
import time
import random
from urllib.error import HTTPError
import signal


# 设置GitHub API的个人访问令牌
# 从这里获取:https://github.com/settings/tokens
access_token = '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'

# 分页参数
page = 1
num = 0
savelastfilename =""
lastfilename = ""
url_with_page = ""
fieldnames = ['项目名称', 'tags', '版本号', '压缩包名','是否有发布']
file = None
writer = None

proxies = {
    "https1": "https://182.204.177.61:4331",
    "https2": "https://140.255.150.253:4361",
    "https3": "https://113.231.18.51:4334",
    "https4": "https://116.7.192.240:43581",
    "https5": "https://121.61.160.170:43311",
    "https6": "https://124.231.69.245:43311",
    "https7": "https://183.128.97.139:43251",
    "https8": "https://124.94.188.113:43341",
    "https9": "https://1.82.107.78:4389",
    "https10": "https://1.82.107.49:4379",
}

headers = {
    'User-Agent': 'Mozilla/5.0',
    'Authorization': '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',
    'Content-Type': 'text/html',
    'Accept': 'application/json'
}


# 定义信号处理函数
def signal_handler(sig, frame):
    print("\n收到了中断信号,程序退出!!!")
    sys.exit(0)

# 注册信号处理函数
signal.signal(signal.SIGINT, signal_handler)

def get_html_url_with_tags(file_name):
    html_urls = []
    Name = ''
    with open(file_name, 'r', newline='') as file:
        reader = csv.DictReader(file)
        for row in reader:
            Name = row.get('Name')
            html_url = row.get('HTML URL')
            if html_url:
                new_html_url = html_url + "/tags"
                html_urls.append(new_html_url)
                #print("Name="+ Name +"  url=" + new_html_url)
    return Name,html_urls

def download_file(url, save_path, timeout=20, max_retries=3):
    retries = 0
    while retries < max_retries:
        try:
            # 发送GET请求获取文件内容,设置超时时间
            #@retry(tries=3, delay=2)  # 重试3次,每次间隔2秒
            #response = requests.get(url, proxies=proxies, headers=headers, timeout=15)
            response = requests.get(url, proxies=proxies, timeout=15)
            # 检查响应状态码
            if response.status_code == 200:
                # 写入文件
                print(f"正在下载文件{save_path}...",end='')
                with open(save_path, 'wb') as f:
                    f.write(response.content)
                print(f"...文件下载完成")
                return True
            else:
                print(f"下载失败:状态码 {response.status_code}")
                #print("下载失败:超过最大重试次数")
                check_connection_error(response)
        except requests.exceptions.Timeout:
            print(f"请求超时,正在尝试重试...", end="")
        except requests.exceptions.RequestException as e:
            print(f"请求异常:{e}", end="")

        retries += 1
        print(f"重试次数:{retries}")
    
    return False

def check_connection_error(response):
    """检查是否由于连接问题而无法访问 GitHub"""
    if response.status_code == 200:
        print("返回200!")
        return True
    if response.status_code == 403:
        print("已达到 GitHub API 请求限制!")
        return False
    elif response.status_code == 400:
        print("服务器无法理解请求!")
        return False
    elif response.status_code == 502:
        print("远程服务器关闭了连接。")
        return False
    elif response.status_code == 404:
        print("未找到请求的资源。")
        return False
    elif response.status_code == 407:
        print("代理服务器需要身份验证!")
        return False
    elif response.status_code == 406:
        print("客户端请求问题,可能是代理失效,建议更换代理IP列表")
        return False
    elif response.status_code == 429:
        print("请求过多,请稍后重试。")
        return False
    elif response.status_code == 504:
        print("网关超时,等等并重试或更换其它代理!")
        return False
    elif response.status_code >= 500:
        print("远程服务器内部错误。")
        return False
    else:
        try:
            print("远程服务器返回未知错误,正在尝试获取更多详细信息...")
            response.raise_for_status()  # 如果请求失败,这将抛出一个 HTTPError 异常
            print("获取详细信息失败,当前状态码为:", response.status_code)
            return False  # 如果无法获取详细信息,则返回 False,表示请求失败
        except HTTPError as e:
            print("发生了 HTTPError 异常:", e)
            return False  # 如果抛出 HTTPError 异常,则返回 False,表示请求失败
        except ConnectionError as ce:
            print("连接被远程服务器关闭,没有返回任何响应。")
            return False  # 如果捕获到 ConnectionError 异常,则返回 False,表示请求失败
        except requests.exceptions.Timeout:
            print("连接超时:可能是由于网络问题导致的连接失败")
            return False
        except requests.exceptions.ConnectionError:
            print("连接错误:无法连接到服务器")
            return False
        except requests.exceptions.RequestException as e:
            print("请求异常,最可能是代理问题:", e)
            return False

def wait_and_retry(wait_time=30):
    """等待一段时间后重试请求"""
    print(f"等待 {wait_time} 秒后重试...")
    time.sleep(wait_time)

def get_github_rate_limit(headers):
    url = "https://api.github.com/rate_limit"
    headers = {
        'User-Agent': 'Mozilla/5.0',
        'Authorization': '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',
        'Content-Type': 'text/html',
        'Accept': 'application/json'
    }
    #response = requests.get(url, proxies=proxies, headers=headers)
    response = requests.get(url, proxies=proxies)
    data = response.json()
    limit = data["rate"]["limit"]
    remaining = data["rate"]["remaining"]
    reset_time = data["rate"]["reset"]
    print(f"限速检查完成...")
    return limit, remaining, reset_time 

def update_ifcheck_value(file_name, Name):
    """打开 CSV 文件,将指定 Name 对应的行的 ifcheck 字段值修改为 1"""
    rows = []
    with open(file_name, 'r', newline='') as file:
        reader = csv.DictReader(file)
        fieldnames = reader.fieldnames  # 获取表头字段名
        for row in reader:
            if row.get('Name') == Name:
                row['ifcheck'] = '1'  # 将 ifcheck 字段值修改为 1
            rows.append(row)
    # 写回 CSV 文件
    with open(file_name, 'w', newline='') as file:
        writer = csv.DictWriter(file, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(rows)

def renamefile(name,filename):
    current_path = os.getcwd()
    old_name = filename
    # 设置新的文件名
    new_name = name + "_" + filename
    # 构建新文件的完整路径
    new_path = os.path.join(current_path, new_name)
    # 构建旧文件的完整路径
    old_path = os.path.join(current_path, old_name)
    # 重命名文件
    if os.path.isfile(new_path):
        return False
    else:
        os.rename(old_path, new_path)
        return True

def analyze_download_links(Name, url):
    global lastfilename,num,headers
    global writer,file
    while True:
        
        #判断是否为多页
        if num == 20:
            url_with_page = url + "?after=" + lastfilename
            num = 0
        else:
            url_with_page = url
        print("访问的url=" + url_with_page)
        
        #判断是否被限速,被限速的话,等待10~20秒
        limit, remaining, reset_time=get_github_rate_limit(headers)
        if remaining == 0:
            count = 1
            time.sleep(random.randint(10, 20))
            print("剩余请求次数为 0 了,现在更新header" )
            headers = {
                'User-Agent': 'User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0',
                'Authorization': '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',
                'Content-Type': 'text/html',
                'Accept': 'application/json'
            }
        
        #通过一个代理列表中中的一个代理获取当前url项目的tags页面
        #response = requests.get(url_with_page,proxies=proxies, headers=headers)
        #直接获取当前url项目的tags页面
        response = requests.get(url_with_page,proxies=proxies)
        #进行容错判断,如果链接错误,则等一会重新链接
        print("进行服务器返回检查中..." )
        if False == check_connection_error(response):
            wait_and_retry()
            print("服务器返回错误,请稍等一会..." )
            #time.sleep(random.randint(10, 20))
            headers = {
                'User-Agent': 'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
                'Authorization': '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',
                'Content-Type': 'text/html',
                'Accept': 'application/json'
            }
            continue
        #如果没有响应,则10~20秒,更换header重新链接
        if response is None:
            # Exit loop if HTTP Error 422
            print("GitHub 未响应")
            time.sleep(random.randint(10, 20))
            headers = {
                'User-Agent': 'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
                'Authorization': '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',
                'Content-Type': 'text/html',
                'Accept': 'application/json'
            }
            continue
        #如果返回200,说明请求正确,处理返回数据
        if response.status_code == 200:
            soup = BeautifulSoup(response.text, 'html.parser')
            #如果项目tags中没有版本,则返回     
            if "There aren’t any releases here" in response.text:
                with open(save_file_name, 'a', newline='') as file:
                    writer = csv.writer(file)
                    writer.writerow([Name, url, '', '','0'])
                print("当前项目tags下无发布版本")
                break
            #H抓取tags页面中所有的下载链接
            download_links = soup.find_all('a', href=lambda href: href and (href.endswith('.zip') or href.endswith('.tar.gz')))
            #print("所有链接:" + download_links)
            if len(download_links) == 0:
                print("当前项目tags下无发布版本")
                break
            #分析每一个链接
            for link in download_links:
                #print("Found download link:", link['href'])
                file_name = os.path.basename(link['href'])  #file_name是压缩包名字

                if os.path.isfile(file_name):
                    print("该项目版本已经下载,略过!")
                    continue
               
                if download_file("https://github.com/" + link['href'], file_name):  #拼接为完整下载链接后下载文件
                    #print(f"确认文件已下载完成")
                    renamefile(Name,file_name)
                    
                    lastfilename, _ = os.path.splitext(file_name)  #lastfilename 是去掉.zip或.gz后的文件名
                    if lastfilename.endswith('.tar'):  #如果后面还有tar后缀
                        lastfilename, _ = os.path.splitext(lastfilename)  #lastfilename 文件名称,实际上版本号
                    #项目名称,tags url、版本号和压缩包文件名,是否找到发布包写入文件。1表示有发布包
                    with open(save_file_name, 'a', newline='') as file:
                        writer = csv.writer(file)
                        writer.writerow([Name, url, lastfilename, file_name,'1'])
                    num = num + 1  #当前下载文件数加1
                else:
                    headers = {
                        'User-Agent': 'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
                        'Authorization': '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',
                        'Content-Type': 'text/html',
                        'Accept': 'application/json'
                    }
                    print(f"更换header重新下载...")
                    download_file("https://github.com/" + link['href'], file_name)  #拼接为完整下载链接后下载文件
                if  num == 20:
                    continue
                #如果有多页,每页返回10个版本,一共20个压缩包,如果不到20表示版本页面不到一页,    
            if num < 20:
                break
        #user_input = input("请输入任意内容,按 Enter 键结束程序:")
        #if user_input:
        #    print("用户输入了ctrl+C:", user_input)
        #
        #break
        else:
            print(f'页面返回不是200时,返回状态码是: {response.status_code}')
            continue
 
# 测试程序
if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("请提供两个参数:第一个参数是项目列表;第二个需要创新的文件用来保存该项目版本信息")
        sys.exit(1)
    
    file_name = sys.argv[1]
    if not file_name.endswith('.csv'):
        print("Please provide a CSV file.")
        sys.exit(1)
    if os.path.exists(file_name):
        print("打开文件,开始分析...") 
    else:
        print("输入文件不存在,请确认")
        sys.exit(1)
    
    save_file_name = sys.argv[2]
    if not save_file_name.endswith('.csv'):
        print("Please provide a CSV save file.")
        sys.exit(1)
    #如果保存文件不存在,则创建文件,添加表头
    if os.path.exists(save_file_name):
        print("保存文件已经存在,会在文件后面追加数据") 
    else:
        with open(save_file_name, 'a', newline='') as file:
            writer = csv.writer(file)
            writer.writerow(fieldnames)

    #Name,html_urls = get_html_url_with_tags(file_name)
    #for url in html_urls:
    Name = ''
    with open(file_name, 'r', newline='') as file:
        reader = csv.DictReader(file)
        for row in reader:
            ifcheck = row.get('ifcheck')
            Name = row.get('Name')
            html_url = row.get('HTML URL')
            if ifcheck == '0' and html_url:
                new_html_url = html_url + "/tags"
                analyze_download_links(Name,new_html_url)
                update_ifcheck_value(file_name, Name)
            #user_input = input("您的输入中断了下载,按Ctrl+C键结束程序,其它键继续下载")
            #if user_input:
             #   print("用户输入了:", user_input)
             #   break
    print("检测完成!") 

————————————————————————————————

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/71778.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ECC升级到S/4 HANA的功能差异 物料、采购、库存管理对比指南

ECC升级到S/4 HANA后&#xff0c;S4 将数据库更换为HANA后性能有一定提升&#xff0c;对于自开发程序&#xff0c;可以同时将计算和部分业务逻辑下推到HANA数据库层&#xff0c;减少应用层和数据库层的交互次数和数据传输&#xff0c;只返回需要的结果到应用层和显示层。提升自…

表格columns拼接两个后端返回的字段(以umi框架为例)

在用组件对前端项目进行开发时&#xff0c;我们会遇到以下情况&#xff1a;项目原型中有取值范围这个表字段&#xff0c;需要存放最小取值到最大取值。 而后端返回给我们的数据是返回了一个最小值和一个最大值&#xff0c; 在columns中我们需要对这两个字段进行拼接&#xff0…

使用Galaxy创建生物信息学工作流的步骤详解

李升伟 整理 Galaxy 是一个基于 Web 的生物信息学平台&#xff0c;提供了直观的用户界面和丰富的工具&#xff0c;帮助用户创建和管理生物信息学工作流。以下是使用 Galaxy 创建生物信息学工作流的主要步骤&#xff1a; 1. 访问 Galaxy 平台 打开 Galaxy 的官方网站&#xff…

蓝桥杯—走迷宫(BFS算法)

题目描述 给定一个NM 的网格迷宫 G。G 的每个格子要么是道路&#xff0c;要么是障碍物&#xff08;道路用 11表示&#xff0c;障碍物用 0 表示&#xff09;。 已知迷宫的入口位置为 (x1​,y1​)&#xff0c;出口位置为 (x2​,y2​)。问从入口走到出口&#xff0c;最少要走多少…

【GPT入门】第12课 FunctionCall 生成数据库sql代码

【GPT入门】第12课 FunctionCall 生成数据库sql代码 1.概述2. 代码3.执行结果 1.概述 如下代码的任务&#xff1a;自然语言问ai,自动生成sql并回答用户 实现思路&#xff1a; 步骤1. ai会把用户的问题&#xff0c;转为sql 步骤2. 程序执行sql 步骤3.把执行的sql结果&#xff…

《白帽子讲 Web 安全》之身份认证

目录 引言 一、概述 二、密码安全性 三、认证方式 &#xff08;一&#xff09;HTTP 认证 &#xff08;二&#xff09;表单登录 &#xff08;三&#xff09;客户端证书 &#xff08;四&#xff09;一次性密码&#xff08;OTP&#xff09; &#xff08;五&#xff09;多因…

服务器python项目部署

角色&#xff1a;root, 其他用户应该也可以 1. 安装python3环境 #如果是新机器&#xff0c;尽量执行&#xff0c;避免未知报错 yum -y update python -v yum install python3 python3 -v2. 使用virtualenvwrapper 创建虚拟环境,并使用workon切换不同的虚拟环境 # 安装virtua…

更新vscode ,将c++11更新到c++20

要在CentOS系统中安装最新版本的GCC&#xff0c;你可以使用SCL&#xff08;Software Collections&#xff09;仓库&#xff0c;它提供了开发工具的最新版本。以下是安装步骤&#xff1a; 1、 添加SCL仓库&#xff1a; 首先&#xff0c;添加CentOS的SCL仓库&#xff0c;该仓库…

Deeplabv3+改进5:在主干网络中添加EMAattention|助力涨点!

🔥【DeepLabv3+改进专栏!探索语义分割新高度】 🌟 你是否在为图像分割的精度与效率发愁? 📢 本专栏重磅推出: ✅ 独家改进策略:融合注意力机制、轻量化设计与多尺度优化 ✅ 即插即用模块:ASPP+升级、解码器 PS:订阅专栏提供完整代码 目录 论文简介 步骤一 步骤二…

基于自监督三维语义表示学习的视觉语言导航

前言 目前的视觉语言导航存在的问题&#xff1a; &#xff08;1&#xff09;在VLN任务中&#xff0c;大多数当前方法主要利用RGB图像&#xff0c;忽略了环境固有的丰富三维语义数据。许多语义无关的纹理细节不可避免地被引入到训练过程中&#xff0c;导致模型出现过拟合问题&…

网络原理之HTTPS(如果想知道网络原理中有关HTTPS的知识,那么只看这一篇就足够了!)

前言&#xff1a;随着互联网安全问题日益严重&#xff0c;HTTPS已成为保障数据传输安全的标准协议&#xff0c;通过加密技术和身份验证&#xff0c;HTTPS有效防止数据窃取、篡改和中间人攻击&#xff0c;确保通信双方的安全和信任。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要…

【江协科技STM32】ADC数模转换器-学习笔记

ADC简介 ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁&#xff0c;ADC是一种将连续的模拟信号转换为离散的数字信号的设备或模块12位逐次逼近型…

文件系统文件管理

文件缓冲区&#xff08;内核级&#xff0c;OS内部的&#xff09;存在的意义&#xff1a;系统调用将数据写入缓冲区后函数即可返回&#xff0c;是从内存到内存的&#xff0c;提高了程序的效率。之后将缓冲区数据刷新到硬盘则是操作系统的事了。无论读写&#xff0c;OS都会把数据…

HTML 标签语义化指南:让网页更易读

HTML 语义化标签是指在 HTML 中使用具有明确含义的标签来标记网页内容的结构和意义。这些标签可以提供更多的语义信息&#xff0c;有助于搜索引擎理解网页内容&#xff0c;并为使用辅助技术的用户提供更好的访问体验。 以下是一些常见的HTML语义化标签及其含义和用途&#xff…

机器学习:线性回归,梯度下降,多元线性回归

线性回归模型 (Linear Regression Model) 梯度下降算法 (Gradient Descent Algorithm) 的数学公式 多元线性回归&#xff08;Multiple Linear Regression&#xff09;

共绘智慧升级,看永洪科技助力由由集团起航智慧征途

在数字化洪流汹涌澎湃的当下&#xff0c;企业如何乘风破浪&#xff0c;把握转型升级的黄金机遇&#xff0c;已成为所有企业必须直面的时代命题。由由集团&#xff0c;作为房地产的领航者&#xff0c;始终以前瞻视野引领变革&#xff0c;坚决拥抱数字化浪潮&#xff0c;携手数字…

laravel es 相关代码 ElasticSearch

来源&#xff1a; github <?phpnamespace App\Http\Controllers;use Elastic\Elasticsearch\ClientBuilder; use Illuminate\Support\Facades\DB;class ElasticSearch extends Controller {public $client null;public function __construct(){$this->client ClientB…

阿里发布新开源视频生成模型Wan-Video,支持文生图和图生图,最低6G就能跑,ComFyUI可用!

Wan-Video 模型介绍&#xff1a;包括 Wan-Video-1.3B-T2V 和 Wan-Video-14B-T2V 两个版本&#xff0c;分别支持文本到视频&#xff08;T2V&#xff09;和图像到视频&#xff08;I2V&#xff09;生成。14B 版本需要更高的 VRAM 配置。 Wan2.1 是一套全面开放的视频基础模型&…

闭包函数是什么?

闭包函数是 JavaScript 中一个非常重要且强大的概念&#xff0c;下面将从定义、形成条件、作用、示例以及优缺点等方面详细介绍闭包函数。 定义 闭包是指有权访问另一个函数作用域中的变量的函数。简单来说&#xff0c;即使该函数执行完毕&#xff0c;其作用域内的变量也不会…

nuxt2 打包优化使用“compression-webpack-plugin”插件

在使用 Nuxt.js 构建项目时&#xff0c;为了提高性能&#xff0c;通常会考虑对静态资源进行压缩。compression-webpack-plugin 是一个常用的 Webpack 插件&#xff0c;用于在生产环境中对文件进行 Gzip 压缩。这对于减少网络传输时间和提高页面加载速度非常有帮助。下面是如何在…