资讯动态

内网环境Python whl包离线下载与安装完整指南

发布时间:2026/10/13 13:36:35 来源:尧图企业网站定制
运维的朋友应该都遇到过这种情况生产环境是内网隔离的服务器上连不上外网。需要装一个 Python 包直接 pip install 根本用不了只能拿着 U 盘在能联网的机器上下载好再拷进去离线安装。但下载哪个文件、怎么找对应 Python 版本的包、装的时候依赖怎么处理每一步都有坑。这篇就完整梳理一下内网环境 Python whl 包下载与离线安装的方法从原理到实操把细节和踩过的坑都写清楚。1. 先搞清楚 whl 包到底是什么whlwheel是 Python 的一种二进制发行格式简单说就是提前帮你把源码编译好的“成品包”。你用 pip install 某个包的时候pip 默认会优先找 whl 格式的包来装找不到才会下载源码包现场编译。1.1 为什么内网环境非要提前下载 whl内网服务器的痛点在于两点一是没有外网连接pip 的源地址根本访问不了二是就算你把源码包拷进去编译这一步也经常失败。比如某些包典型的如 Numpy、Pandas、psutil 这类带了 C 扩展用源码包安装需要服务器上有完整的编译工具链gcc、python 头文件、依赖库等生产环境不可能给你装这些而且编译时间很长还容易报错。whl 包就不一样它已经是编译好的二进制文件安装时本质上就是解压拷贝到对应目录不需要编译器。所以提前在外网机器上把目标平台对应的 whl 和依赖全都下载好内网安装才能做到既快又稳。1.2 whl 文件名的命名规则怎么看以某个包为例psutil-5.9.0-cp37-cp37m-manylinux2014_x86_64.whl拆开看psutil包名5.9.0版本号cp37目标 Python 版本是 3.7cp37m对应的 Python 解释器 ABI 版本带 m 表示支持 Unicode 宽字符manylinux2014_x86_64平台标识表示支持 Linux 64 位这里最关键的是后面三段Python 版本、ABI 标识、平台标识。三者都要匹配才能装上去。这也是内网下载 whl 时最容易出错的地方——只看包名和版本忽视平台差异结果拷过去装不上。1.3 内网服务器环境怎么确认目标平台信息下载之前先确认服务器这边的几个关键信息。直接在服务器上跑python --version uname -m cat /etc/os-release第一行确认 Python 版本比如 3.6 还是 3.8这决定了你要找cp36还是cp38的包。第二行确认 CPU 架构一般 x86_64 居多也有少部分机器是 arm64aarch64。第三行确认操作系统发行版和版本比如 CentOS 7、Ubuntu 20.04 等这影响 manylinux 标签的选择。我见过有人不看架构在 x86 服务器上拿着 arm64 的包装报错信息五花八门。花两分钟确认这几个信息后面能省一晚上的时间。2. 内网下载 whl 包前的准备工作准备工作做得好不好直接影响后面能不能一次装成功。核心原则是把“依赖解析”这件事尽量放在外网机器上完成让 pip 自己分析好需要哪些依赖然后统一下载而不是一个个手动找。2.1 外网准备机器的环境条件用来下载的机器需要满足三个条件能访问外网网络通畅Python 版本与内网服务器一致操作系统与内网服务器一致或至少是同一大版本其中第二点是最重要的很多人在这一步翻车。假设内网服务器是 Python 3.8你就必须找一个 Python 3.8 的外网机器或者用 pyenv、conda 切换出 3.8 环境来执行下载。因为 pip 在做依赖解析时有些包的依赖项会根据 Python 版本变化如果版本不一致下载的依赖列表可能对不上。操作系统最好也是一致的。你在一台 Ubuntu 上用 pip download 下载的包和 CentOS 上识别到的 manylinux 标签可能就有差异导致部分依赖漏下载或者选错标签。2.2 准备好一个干净的下载目录和索引源建议单独建一个目录把下载的 whl 文件集中存放mkdir -p /data/pip_packages cd /data/pip_packages索引源方面国内网络环境下建议直接用镜像源比如清华、阿里云的 PyPI 镜像。至于为什么不直接连官方源一方面是速度问题另一方面是官方源某些资源国内访问不稳定在公司网络环境下更容易超时。下载之时可以把镜像源地址作为参数传入不改变全局配置更干净pip download -i https://pypi.tuna.tsinghua.edu.cn/simple 包名版本号2.3 常见依赖问题的提前规避思路其实很多内网装包失败不是因为主包装不上而是因为依赖解析不完整。举个例子你要装requests它依赖urllib3、charset_normalizer、idna、certifi。如果你只下载 requests 的 whl 而漏掉这四个依赖内网机器上 pip install 会自动去源上找但内网没源直接就失败。所以前述原则非常重要一定让 pip 自己去解析依赖用 pip download 完整拉取而不是手动一个个下载。到这一步你还需要关注后续章节中“依赖是否会带源码包”的问题提前加参数限制。3. 在联网机器上下载 whl 包的完整实操这一步直接关系到内网机器能不能顺利安装。我用的方法是用 pip download 命令让 pip 帮忙解析依赖并按目标平台下载然后统一拷贝。下面拆开细讲。3.1 基础命令的完整说明pip download 的语法和 pip install 基本一致只是多了几个控制下载目标的参数。最基本的用法pip download -d /data/pip_packages requests2.28.2-d指定下载目录后面的参数指定包名和版本号。这条命令会下载 requests 本身以及它所有依赖到指定目录。但我一般不直接这么跑因为会顺便下载很多无关的东西。实际会用一组更完整的参数pip download \ -d /data/pip_packages \ --only-binary:all: \ --platform manylinux2014_x86_64 \ --python-version 38 \ -i https://pypi.tuna.tsinghua.edu.cn/simple \ requests2.28.2解释一下几个关键参数--only-binary:all:只下载二进制 wheel 包禁止下载源码包sdist。因为源码包到了内网还得现场编译完全背离了我们的初衷。--platform指定目标部署平台。很多 linux 平台解析出来的名称会不一样这里以目标的 manylinux 标识来定。x86_64 的机器基本用manylinux2014_x86_64即可需要更精确的要看服务器系统版本。--python-version指定目标 Python 版本。这里填 38 表示 Python 3.8不需要写小数点。-i指定镜像源加快下载速度。加上这些参数之后pip 就会以“目标机器”的视角去解析依赖和选择包版本而不是以“当前下载的这台机器”的视角。这样做才是最稳妥的。3.2 指定 only-binary 的深层原因为什么要强制--only-binary:all:因为有些包在特定平台没有提供对应的二进制 wheel或者发布方只提交了源码包。比如某些较冷门的库对 Python 新版本没来得及发布编译好的 wheelpip 此时会退而求其次去下载 sdist 源码包。源码包下载回来后你还不知道直到内网安装时出现 gcc 报错才意识到问题。强制 only-binary 之后如果某个包没有对应的二进制包pip 会直接报错告诉你“找不到匹配的版本”而不是默默下载源码包。这样问题就暴露在外网阶段处理起来成本低得多。注意--only-binary:all:会同时禁止所有依赖包下载源码包。如果你的目标平台比较特殊、某些依赖确实只有源码包报错时需要考虑是否真的需要调整策略而不是直接去掉这个参数。多数情况优先排查版本兼容性而不是打开源码包开关。3.3 指定 platform 参数的注意事项platform 参数是最容易搞错的地方。manylinux2014_x86_64是常见的选择但不同系统的实际支持范围不一样CentOS 7 及更早版本考虑到系统自带的 glibc 版本较老建议用manylinux2014_x86_64或者兼容性更好的manylinux1_x86_64CentOS 8 / Rocky Linux 8 / Ubuntu 20.04glibc 版本较新manylinux2014_x86_64基本都能用Ubuntu 22.04 / Debian 12有些包还会提供manylinux_2_28_x86_64或更新的标签如果没有manylinux2014_x86_64依然兼容如果你不确定用哪个可以先用--platform manylinux2014_x86_64试试如果下载阶段提示找不到匹配的包再尝试降低兼容性级别比如 manylinux1或者调整搜索源。3.4 如何检查下载下来的包是否正确下载完成后不要急着拷贝先看一眼目录里有哪些文件ls -l /data/pip_packages/重点检查两件事第一目录里是不是全部为.whl文件。如果出现了.tar.gz结尾的包说明有依赖没有二进制版本被降级成了源码包这个包建议处理一下——要么升级该依赖到支持二进制的版本要么先换个依赖版本组合。第二随机抽查几个 whl 的文件名。确认 cpXX 是否对应你的 Python 版本、平台标签是否符合目标平台架构。比如前面提到的 psutil 文件名里cp37-cp37m-manylinux2014_x86_64如果服务器是 Py3.8那就不能直接拿来用要找cp38-cp38的版本。再把纯离线情况下安装阶段的注意事项提前说一下whl 包拷贝进内网之后安装的顺序其实不用太操心pip 会自动处理依赖顺序你只要把全部 whl 放在同一个目录然后一条命令装完即可。4. 纯离线环境下安装 whl 包的实操过程内外网之间如果只有单向传输比如通过 U 盘拷贝或者内部文件传输系统整体思路也比较清晰把外网机器上下载好的 whl 目录整体打包传到内网服务器上解压后离线安装。这个过程我用过很多次下面把具体步骤写清楚。4.1 打包并传输先把 whl 目录打包方便传输cd /data tar czf pip_packages.tar.gz pip_packages然后通过存储介质或者公司内部的传输通道拷到内网服务器。内网服务器的目录结构无所谓你喜欢放哪就放哪比如我们一般放在/opt/packages/mkdir -p /opt/packages tar xzf pip_packages.tar.gz -C /opt/packages解压之后确认一下文件是否完整重点看有多少个 whl 文件、有没有文件在传输过程中损坏find /opt/packages/pip_packages -name *.whl | wc -l4.2 离线安装命令与参数离线安装和普通安装的差异主要在源上。因为内网没有外网pip 检查源会超时失败需要通过--no-index明确告诉 pip 不要去找远程源只从本地目录找pip install --no-index --find-links/opt/packages/pip_packages 包名版本号关键参数--no-index不去访问 PyPI 索引源--find-links指定本地 whl 包的搜索目录其实你也可以直接指定本地目录下某一个人 whl 文件来安装但那样容易漏依赖。最推荐的方式还是上面这种让 pip 自己在指定目录里搜索自动解析依赖。pip 在本地目录中就能完成依赖检查和你在线装包一样省心。如果需要安装的不是单个包而是目录里的所有包也可以这样pip install --no-index --find-links/opt/packages/pip_packages *.whl但这条命令有个限制它只是把当前目录下所有 whl 安装一遍并不会自动先安装依赖后安装主包。如果目录里的包之间存在依赖关系乱序安装可能会报错说“某个依赖不存在”。实际工作中我会先安装依赖再安装主包或者干脆省心一点用前一种按包名逐个装的方法。4.3 安装时校验实际效果安装完成后不要直接就跑业务先到 Python 里验证一遍python -c import requests; print(requests.__version__)顺手把依赖关键包也验证一遍。这一步能确认 whl 包确实装到了当前正在使用的 Python 环境里而不是因为 PATH 问题导致装到了别的环境的 site-packages。如果项目里有多个 Python 环境比如虚拟环境安装时要把环境激活后再执行 pip install。虚拟环境和系统全局环境之间的差异也是日常排查中经常遇到的问题后面在常见问题部分会展开。4.4 安装失败的常见情况预判断安装阶段最常见的报错是这几种提示“Could not find a version that satisfies the requirement …”说明本地目录里缺少该包对应的版本大概率是外网下载阶段依赖解析不完整需要回到外网重新下载。提示“is not a supported wheel on this platform”说明 whl 文件名的平台标签与当前环境不匹配检查 cpXX、manylinux 标签以及架构。提示“No matching distribution found”和第一种类似可能是版本号没对上也可能缺少某个依赖。与其到了内网再排查不如在外网下载阶段就把上述问题都规避掉。这也是我反复强调下载阶段就应该做完整校验的原因。5. 不同场景下的下载策略对比下载 whl 这个事看起来就是一条 pip download 命令但实际应用场景不同策略差别很大。下面针对几种常见的内网服务器场景整理一下。5.1 单包依赖少的小项目场景如果内网服务器只需要安装一两个功能单纯的包比如操作 HTTP 的 requests、处理时间的 arrow这种依赖少、自身比较简单的包直接按前面的完整参数下载即可pip download \ -d /data/pip_packages \ --only-binary:all: \ --platform manylinux2014_x86_64 \ --python-version 38 \ requests2.28.2下载完成的文件数量不会太多拷贝即可。5.2 涉及深度学习或科学计算的包场景如果内网服务器要部署的是涉及深度学习的服务麻烦程度会高一个量级。以 PyTorch 为例它不只是一个包还会依赖 Numpy、OpenCV 等一大堆底层库而且 PyTorch 的 whl 往往不在 PyPI 上而是在独立源发布的。比如 PyTorch CPU 版本在 Linux 上的推荐安装命令一般长这样pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu这样的源和 PyPI 是两套体系。这种情况下我的建议是分两步走第一步在能联网的机器上先创建一个虚拟环境并安装好python -m venv torch_env source torch_env/bin/activate pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu第二步用 pip download 把虚拟环境里已经装好的包对应的版本下载下来pip download \ -d /data/pip_packages \ --only-binary:all: \ --platform manylinux2014_x86_64 \ --python-version 38 \ torch版本 \ torchvision版本 \ torchaudio版本另外深度学习相关的镜像源还有 huggingface 等下载模型权重时类似。5.3 多架构服务器集群场景内网环境中服务器架构不统一的情况并不少见一批 x86_64 的机器跑业务几台 ARM64aarch64的机器做边缘计算。这时候就不能只下一套 whl得分别下载、分别存放、分别安装。对外网下载机器来说需要分别执行两套参数# x86_64 架构 pip download -d /data/pip_packages/x86_64 \ --only-binary:all: \ --platform manylinux2014_x86_64 \ --python-version 38 \ 包名版本 # ARM64 架构 pip download -d /data/pip_packages/aarch64 \ --only-binary:all: \ --platform manylinux2014_aarch64 \ --python-version 38 \ 包名版本注意 platform 参数要对应架构。manylinux2014_aarch64就是 ARM64 Linux 对应的标签。拷贝的时候不同架构的 whl 包分别拷到对应架构的服务器上避免混用。5.4 多 Python 版本共存环境场景有些内网服务器上同时存在 Python 3.6、3.8、3.10 三个版本分别跑不同的业务。这时下载 whl 也要严格区分 Python 版本。最省事的做法是为每个 Python 版本单独建目录、单独执行下载pip download -d /data/pip_packages/py36 \ --only-binary:all: \ --platform manylinux2014_x86_64 \ --python-version 36 \ 包名版本 pip download -d /data/pip_packages/py310 \ --only-binary:all: \ --platform manylinux2014_x86_64 \ --python-version 310 \ 包名版本下载之后在对应的 Python 环境里安装时只用对应目录的包。千万不要混用cp37 的包装在 Python 3.10 环境里基本都会报平台不支持。6. 常见问题与排查技巧实录这一部分梳理一些我在实操中遇到的问题有一些是显性的报错有一些是隐性问题等发现问题时已经在内网装了半天了。6.1 纯离线安装时的常见报错速查表报错信息原因分析解决方案Could not find a version that satisfies the requirement XXX本地 whl 目录里缺少该包或对应版本回外网机器重新执行 pip download确认依赖完整后再传输XXX is not a supported wheel on this platform文件名里的 cpXX 或者平台标签与当前环境和架构不匹配核对服务器 Python 版本、架构重新下载对应平台 whlNo matching distribution found for XXX依赖解析阶段就漏掉了这个包用 pip download 完整拉取不要手动下载主包忽略依赖error: command ‘gcc’ failed装到了源码包sdist现场编译失败检查下载目录是否混有 .tar.gz重新用--only-binary:all:下载Failed to establish a new connectionpip 尝试访问网络源失败确认是否加了--no-index内网环境不要省略这个参数6.2 下载阶段的疑难问题定位下载阶段看起来简单实际也容易出问题。最典型的一种是明明指定了--platform manylinux2014_x86_64但 pip 还是提示找不到对应版本的包。这种问题的原因往往是目标包的主版本太新发布方在较新的版本中不再提供 manylinux2014 标签的二进制 wheel只提供 manylinux_2_28 或者更新的标签。比如某些包在 2024 年之后的版本陆续不再支持 old glibc 的 manylinux2014。这时有两条路第一降低主包版本选一个仍然提供 manylinux2014 wheel 的版本。这个最沿用且高效。第二用更新的 platform 标签重新下载但前提是内网服务器的 glibc 版本足够新。如何确认 glibc 版本ldd --version如果输出显示 glibc 2.28 及以上就可以试试--platform manylinux_2_28_x86_64。还有一种情况某个包在 PyPI 上确实没有对应平台的二进制只有源码包。比如一些冷门包长期没有构建 Linux 的 wheel。这时候如果你坚持 only-binary就会直接报错找不到。我的处理方式是要么在源码有提供编译产物的替代渠道寻找比如项目自己的 release 附件要么退而求其次解决编译器依赖问题但这应该尽量避免。6.3 虚拟环境与全局环境混淆问题内网服务器上常见的情况是既有系统 Python又有项目自带的虚拟环境venv 或者 conda 环境。拷贝 whl 后安装时如果不小心在系统全局环境里装了后面才发现项目实际用的是虚拟环境那就白装了。建议操作顺序是# 先激活目标虚拟环境 source /opt/myproject/venv/bin/activate # 确认当前 Python 路径 which python which pip # 再用 pip 离线安装 pip install --no-index --find-links/opt/packages/pip_packages 包名版本确认which python指向虚拟环境路径后再开始安装能避免装错环境。6.4 版本锁定与一致性校验技巧在操作过程中我强烈建议把下载的包以及版本统一记录到一个 requirements 文件里。你可以先用 pip freeze 导出当前环境中的版本信息pip freeze requirements.txt然后内网安装时可以用-r参数按这个文件逐个安装pip install --no-index --find-links/opt/packages/pip_packages -r requirements.txt但要注意requirements.txt中的版本号必须和下载的 whl 包版本一致否则 pip 在本地找不到对应版本时会报错。如果某些包版本不对手动改一下 requirements.txt 里的版本号即可。提示requirements.txt 中的包版本建议在下载 whl 前就锁定。用pip download下载时比如先指定好-r requirements.txt这样 pip 会自动按照该文件里锁定的每个版本及依赖下载对应 whl。6.5 磁盘空间与文件数量问题有时需要下载的依赖很多Numpy、OpenCV、PyTorch 这些重量级包动辄几百 MB一整套下来可能超过 2GB。外网准备机的磁盘、内网部署服务器的磁盘都要提前确认空间df -h另外文件数量多的时候U 盘传输或内部传输系统可能会限制单包大小。超过单个文件大小限制时建议拆分压缩# 拆分压缩每卷 1GB tar czf - pip_packages | split -b 1024m - pip_packages.tar.gz.然后到内网服务器合并解压cat pip_packages.tar.gz.* | tar xzf -这个办法也确实遇到过几次大文件传输提早拆一下能省事不少。7. 把整套流程固化成可复用的工具脚本下载 whl 这个流程做完一两遍之后其实很机械。如果经常要面对内网部署任务建议直接把它固化成一个小脚本以后换个包名、换个版本号就能直接用。7.1 下载脚本示例一个简单但完整的下载脚本大概长这样#!/bin/bash # 用法: ./download_pkgs.sh requests2.28.2 numpy1.24.3 PKGS$1 PY_VERSION38 PLATFORMmanylinux2014_x86_64 OUTPUT_DIR/data/pip_packages MIRRORhttps://pypi.tuna.tsinghua.edu.cn/simple mkdir -p $OUTPUT_DIR pip download \ -d $OUTPUT_DIR \ --only-binary:all: \ --platform $PLATFORM \ --python-version $PY_VERSION \ -i $MIRROR \ $PKGS使用示例./download_pkgs.sh requests2.28.27.2 安装脚本示例对应的离线安装脚本#!/bin/bash # 用法: ./install_local.sh requests2.28.2 PKGS$1 PKG_DIR/opt/packages/pip_packages pip install --no-index --find-links$PKG_DIR $PKGS脚本中$PKGS展开为空格分隔的多个包自然不会出错。如果遇到复杂依赖、多个包一起装整个流程会顺畅很多。有了这两个脚本处理内网离线安装的整体时间能压缩到几分钟级主要时间花在等待下载完成。8. 关于下载与离线安装的几点经验总结多的不讲了挑几个印象最深的经验收个尾。第一下载阶段多花十分钟内网安装阶段就能少折腾几个小时。强制--only-binary:all:、指定目标平台、用镜像源这几件事不要嫌麻烦。源码包一旦混进去内网那边编译报错排查起来非常痛苦。第二版本锁定这个习惯值得坚持尤其对做长期维护的部署环境保证所有机器的依赖版本一致是后续排查问题的基础。我在实际维护中见过不少因为各服务器依赖版本不一致导致的行为差异问题最后都归结到安装时没严格版本锁定。第三内网服务器如果过段时间需要加装新的 Python 包建议不管之前下载过程序如何全部统一遵循同样的流程重走一遍外网下载、完整校验、拷贝、离线安装。千万不要偷懒手动从别的机器拷一个 whl 过来少一个依赖就够你折腾半天。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑