boxmoe_header_banner_img

Hope`Chen

加载中

文章导读

Python依赖库下载与离线安装程序使用说明


avatar
haotianit 2026年1月11日 92
摘要
还在为Python项目依赖安装失败而头疼?网络不稳定、离线环境、镜像源太慢,统统有解了!本文介绍一款专为开发者打造的Python依赖管理神器,支持自动分析requirements.txt或import语句,一键下载离线安装包,并在无网络环境下快速部署。内置8大常用镜像源,智能测速选最快源,多线程下载安装,还带简洁GUI界面,操作零门槛。无论你是在本地开发、远程部署还是教学演示,它都能大幅提升效率。想知道如何离线批量安装依赖、避免重复安装、解决常见报错?这篇实用指南全讲清楚了。
— 文章内容摘要

1. 程序简介

本程序是一个用于Python项目依赖库管理的工具,主要功能包括自动分析项目依赖、下载离线安装包以及离线安装依赖库。它可以帮助开发者在没有网络或网络环境较差的情况下,快速安装项目所需的依赖库。

2. 功能特点

  • 自动依赖分析:支持从项目的requirements.txt文件或Python源代码的import语句自动分析依赖
  • 镜像源管理:内置8个常用Python镜像源,支持手动选择或自动测试最快镜像
  • 离线包下载:可以将依赖库下载为离线安装包,方便在无网络环境下使用
  • 智能安装:自动检测已安装的依赖,跳过已安装的包,只安装缺失的依赖
  • 多线程支持:使用多线程进行安装和下载,提高效率
  • 友好的GUI界面:提供直观易用的图形用户界面,操作简单

3. 系统要求

  • 操作系统:Windows 7及以上
  • Python版本:3.8及以上
  • 依赖:
    • tkinter(Python标准库,用于GUI界面)
    • urllib.request(Python标准库,用于镜像速度测试)
    • subprocess(Python标准库,用于调用pip命令)

4. 运行方法

  1. 确保已安装Python 3.8或更高版本
  2. 下载程序文件dependency_manager.py
  3. 双击运行或在命令行中执行:
python dependency_manager.py

5. 使用步骤

5.1 选择项目路径

  1. 在”项目路径”区域点击”浏览”按钮
  2. 选择要分析的Python项目目录
  3. 点击”分析依赖”按钮开始分析项目依赖

5.2 分析依赖

程序会自动分析项目依赖,优先读取项目中的requirements.txt文件,如果没有则通过分析Python文件的import语句来获取依赖。分析结果会显示在”依赖库列表”中,包括依赖名称、版本要求和安装状态。

5.3 镜像源设置

  1. 在”镜像源设置”区域,可以通过下拉菜单手动选择镜像源
  2. 点击”自动选择最快镜像”按钮,程序会自动测试所有镜像源的速度,并选择最快的镜像源
  3. 支持的镜像源包括:
    • 官方源
    • 阿里云
    • 腾讯云
    • 华为云
    • 清华大学
    • 北京大学
    • 豆瓣
    • 中科大

5.4 下载依赖

  1. 在”离线包存储路径”区域点击”浏览”按钮,选择离线包存储目录
  2. 在”依赖库列表”中选择要下载的依赖(可使用”全选”按钮选择所有依赖)
  3. 点击”下载选中依赖”或”下载全部依赖”按钮开始下载
  4. 下载完成后,离线包会保存在指定的目录中

5.5 安装依赖

5.5.1 在线安装
  1. 在”依赖库列表”中选择要安装的依赖
  2. 点击”安装选中依赖”按钮开始在线安装
  3. 程序会使用选定的镜像源下载并安装依赖
5.5.2 离线安装
  1. 点击”选择离线包目录安装”按钮
  2. 选择包含离线安装包的目录
  3. 程序会自动安装目录中的所有离线包,跳过已安装的包

6. 常见问题

6.1 分析依赖失败

  • 检查项目路径是否正确
  • 确保项目中包含Python文件或requirements.txt文件
  • 检查Python文件是否使用了正确的import语法

6.2 下载或安装失败

  • 检查网络连接是否正常
  • 尝试更换镜像源
  • 检查权限是否足够(特别是在安装到系统Python目录时)
  • 确保离线包目录中包含有效的安装包(.whl或.tar.gz文件)

6.3 镜像速度测试失败

  • 检查网络连接是否正常
  • 确保防火墙没有阻止程序访问网络
  • 尝试手动选择镜像源

7. 注意事项

  1. 本程序使用系统中的Python解释器执行pip命令,确保系统中已安装pip
  2. 离线安装时,建议使用与目标环境相同的Python版本下载离线包
  3. 程序会自动跳过已安装的依赖,无需担心重复安装
  4. 镜像速度测试结果可能会受到网络环境的影响,建议在网络稳定时进行测试
  5. 对于大型项目,依赖分析可能需要一些时间,请耐心等待
  6. 在Windows系统中,建议以管理员身份运行程序,以避免权限问题

8. 更新日志

  • 版本1.0
    • 初始版本,支持基本的依赖分析、下载和安装功能
  • 版本1.1
    • 添加了镜像源选择功能
    • 支持自动测试最快镜像
    • 优化了GUI界面
  • 版本1.2
    • 改进了依赖分析算法
    • 增强了错误处理
    • 提高了程序稳定性
# -*- coding: utf-8 -*-
"""
Python依赖库下载与离线安装程序 - 精简核心代码
只包含最关键的功能实现,去掉了完整的UI设计
"""
 
import subprocess
import sys
import os
import threading
import re
import time
import urllib.request
 
# -------------------
# 隐藏CMD窗口的关键代码
# -------------------
if sys.platform == 'win32':
    STARTUPINFO = subprocess.STARTUPINFO()
    STARTUPINFO.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    STARTUPINFO.wShowWindow = 0  # SW_HIDE
 
# -------------------
# 核心功能:获取正确的Python解释器路径
# -------------------
def get_python_exe():
    """获取正确的Python解释器路径,避免PyInstaller打包后调用自身"""
    python_exe = sys.executable
    # 检查是否为PyInstaller打包环境
    if getattr(sys, 'frozen', False):
        # 尝试从注册表获取Python安装路径
        try:
            import winreg
            python_versions = ['3.8', '3.9', '3.10', '3.11', '3.12']
            for version in python_versions:
                try:
                    with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, f"SOFTWARE\Python\PythonCore\{version}\InstallPath") as key:
                        python_path = winreg.QueryValueEx(key, "ExecutablePath")[0]
                        if os.path.exists(python_path):
                            return python_path
                except Exception:
                    continue
        except Exception:
            pass
         
        # 尝试使用环境变量
        env_python = os.environ.get('PYTHONHOME')
        if env_python and os.path.exists(os.path.join(env_python, 'python.exe')):
            return os.path.join(env_python, 'python.exe')
         
        # 回退到系统默认python
        return 'python'
    return python_exe
 
# -------------------
# 核心功能:依赖分析
# -------------------
def analyze_from_imports(project_path):
    """从Python文件的import语句分析依赖"""
    imports = set()
     
    # 获取Python标准库模块列表
    def get_stdlib_modules():
        # 简化版本,只包含常用标准库
        return {
            'abc', 'argparse', 'array', 'ast', 'asyncio', 'base64', 'bisect', 'builtins',
            'collections', 'datetime', 'decimal', 'difflib', 'enum', 'functools', 'gc',
            'glob', 'hashlib', 'heapq', 'http', 'importlib', 'inspect', 'io', 'itertools',
            'json', 'logging', 'math', 'multiprocessing', 'os', 'pathlib', 're', 'shutil',
            'socket', 'sqlite3', 'ssl', 'string', 'subprocess', 'sys', 'threading',
            'time', 'tkinter', 'urllib', 'uuid', 'warnings', 'xml', 'zipfile'
        }
     
    stdlib_modules = get_stdlib_modules()
     
    for root, dirs, files in os.walk(project_path):
        # 跳过虚拟环境和版本控制目录
        dirs[:] = [d for d in dirs if d not in ['venv', 'env', '.venv', '__pycache__', '.git', 'node_modules']]
         
        for file in files:
            if file.endswith('.py'):
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                        content = f.read()
                        # 匹配import语句
                        import_matches = re.findall(r'^import\s+([a-zA-Z0-9_]+)', content, re.MULTILINE)
                        from_matches = re.findall(r'^from\s+([a-zA-Z0-9_]+)', content, re.MULTILINE)
                        imports.update(import_matches)
                        imports.update(from_matches)
                except Exception:
                    continue
     
    # 过滤标准库,映射模块名到包名
    def module_to_package(module_name):
        mapping = {
            'cv2': 'opencv-python', 'PIL': 'Pillow', 'sklearn': 'scikit-learn',
            'yaml': 'PyYAML', 'bs4': 'beautifulsoup4', 'dotenv': 'python-dotenv'
        }
        return mapping.get(module_name, module_name)
     
    third_party = []
    for imp in imports:
        if imp.lower() not in [m.lower() for m in stdlib_modules]:
            pkg_name = module_to_package(imp)
            if pkg_name:
                third_party.append((pkg_name, ""))
     
    return list(set(third_party))
 
# -------------------
# 核心功能:镜像源管理与速度测试
# -------------------
class MirrorManager:
    def __init__(self):
        self.mirror_sources = {
            "官方源": "https://pypi.org/simple",
            "阿里云": "https://mirrors.aliyun.com/pypi/simple/",
            "腾讯云": "https://mirrors.cloud.tencent.com/pypi/simple/",
            "华为云": "https://mirrors.huaweicloud.com/repository/pypi/simple/",
            "清华大学": "https://pypi.tuna.tsinghua.edu.cn/simple/",
            "北京大学": "https://mirrors.pku.edu.cn/pypi/simple/",
            "豆瓣": "https://pypi.doubanio.com/simple/",
            "中科大": "https://pypi.mirrors.ustc.edu.cn/simple/"
        }
        self.selected_mirror = "官方源"
     
    def test_mirror_speed(self, mirror_name, mirror_url):
        """测试单个镜像源的响应速度"""
        try:
            start_time = time.time()
            # 测试URL,选择pip包的简单路径
            test_url = f"{mirror_url.rstrip('/')}/pip/"
            with urllib.request.urlopen(test_url, timeout=5) as response:
                response.read(1024)  # 只读取少量数据
            end_time = time.time()
            return mirror_name, end_time - start_time
        except Exception:
            return mirror_name, float('inf')
     
    def select_fastest_mirror(self):
        """测试所有镜像源并选择最快的"""
        results = []
         
        for mirror_name, mirror_url in self.mirror_sources.items():
            name, speed = self.test_mirror_speed(mirror_name, mirror_url)
            if speed != float('inf'):
                results.append((name, speed))
         
        if results:
            results.sort(key=lambda x: x[1])
            fastest_mirror = results[0][0]
            self.selected_mirror = fastest_mirror
            return fastest_mirror, results[0][1]
        return None, None
 
# -------------------
# 核心功能:依赖下载与安装
# -------------------
def download_packages(packages, download_path, mirror_manager):
    """下载包"""
    os.makedirs(download_path, exist_ok=True)
    python_exe = get_python_exe()
     
    for name, version in packages:
        pkg_spec = f"{name}{version}" if version else name
        print(f"正在下载: {pkg_spec}")
         
        try:
            cmd = [
                python_exe, "-m", "pip", "download",
                pkg_spec,
                "-d", download_path,
                "--no-cache-dir"
            ]
            # 添加镜像源参数
            mirror_url = mirror_manager.mirror_sources[mirror_manager.selected_mirror]
            cmd.extend(["--index-url", mirror_url])
             
            result = subprocess.run(
                cmd, capture_output=True, text=True, encoding='utf-8',
                startupinfo=STARTUPINFO
            )
             
            if result.returncode == 0:
                print(f"✓ {pkg_spec} 下载成功")
            else:
                print(f"✗ {pkg_spec} 下载失败: {result.stderr}")
        except Exception as e:
            print(f"✗ {pkg_spec} 下载出错: {e}")
 
def install_online_packages(packages, mirror_manager):
    """在线安装包"""
    python_exe = get_python_exe()
     
    # 获取已安装包列表
    def get_installed_packages():
        try:
            result = subprocess.run(
                [python_exe, "-m", "pip", "list", "--format=freeze"],
                capture_output=True, text=True, encoding='utf-8',
                startupinfo=STARTUPINFO
            )
            installed = {}
            for line in result.stdout.strip().split('\n'):
                if '==' in line:
                    name, version = line.split('==')
                    installed[name.lower()] = version
            return installed
        except Exception:
            return {}
     
    installed = get_installed_packages()
     
    for name, version in packages:
        pkg_spec = f"{name}{version}" if version else name
         
        # 检查是否已安装
        if name.lower() in installed:
            installed_version = installed[name.lower()]
            if version and version.startswith('=='):
                required_version = version[2:]
                if installed_version == required_version:
                    print(f"⊘ {name}=={installed_version} 已安装相同版本,跳过")
                    continue
            elif not version:
                print(f"⊘ {name} 已安装({installed_version}),跳过")
                continue
         
        print(f"正在安装: {pkg_spec}")
         
        try:
            cmd = [python_exe, "-m", "pip", "install", pkg_spec]
            # 添加镜像源参数
            mirror_url = mirror_manager.mirror_sources[mirror_manager.selected_mirror]
            cmd.extend(["--index-url", mirror_url])
             
            result = subprocess.run(
                cmd, capture_output=True, text=True, encoding='utf-8',
                startupinfo=STARTUPINFO
            )
             
            if result.returncode == 0:
                print(f"✓ {pkg_spec} 安装成功")
            else:
                print(f"✗ {pkg_spec} 安装失败: {result.stderr}")
        except Exception as e:
            print(f"✗ {pkg_spec} 安装出错: {e}")
 
# -------------------
# 使用示例
# -------------------
if __name__ == "__main__":
    # 1. 分析项目依赖
    project_path = "./"
    dependencies = analyze_from_imports(project_path)
    print(f"分析到的依赖: {dependencies}")
     
    # 2. 选择最快镜像源
    mirror_manager = MirrorManager()
    fastest_mirror, speed = mirror_manager.select_fastest_mirror()
    if fastest_mirror:
        print(f"最快的镜像源是: {fastest_mirror} (响应时间: {speed:.3f}秒)")
     
    # 3. 下载依赖
    # download_path = "./offline_packages"
    # download_packages(dependencies, download_path, mirror_manager)
     
    # 4. 安装依赖
    # install_online_packages(dependencies, mirror_manager)

程序截图:

打包好的可执行文件(因涉及OS库,所以可能会有误报情况,介意的请直接用源码自行编译),源码在附件中。

蓝奏云网盘


评论(11)

查看评论列表
评论头像
老街灯影 2026年01月12日
这个工具对有内网环境限制的场景应该挺有用的
评论头像
威严的龙族 2026年01月16日
镜像源测试功能挺实用的,不用手动一个个试速度了
评论头像
qīngyùn 2026年01月19日
有人试过在Python 3.7上跑得起来吗?
评论头像
民国风 2026年01月22日
感觉离线安装部分讲得不够细,要是能支持版本锁定就更好了
评论头像
蠪侄噬影 2026年01月22日
之前给客户部署就遇到过内网装依赖麻烦死,这工具能省不少事
评论头像
暗鸦契约 2026年01月30日
GUI界面看着还行,就是不知道稳定性怎么样
评论头像
阳光灿烂的日子 2026年01月31日
代码里那个跳过虚拟环境目录的逻辑挺细心的
评论头像
心剑客 2026年02月02日
下载路径能选网络驱动器吗?
评论头像
孤岛乘客 2026年02月02日
对于打包成exe会误报这点有点劝退啊
评论头像
混世魔王 2026年02月03日
自动分析import语句这个功能靠谱不?会不会漏掉一些动态导入的库
评论头像
全场最嗨王 2026年02月07日
要是能集成到IDE里当个插件就更方便了

发表评论