☰
CentOS 7 离线安装 requests 库:依赖准备与内网部署实践
2026/10/14 1:41:58 网站建设 项目流程

生产内网里面装东西,最烦的从来不是命令本身,而是前置依赖。上周运维给了一台 CentOS 7 机器,业务脚本要用 Python 写一组内部数据上报,第一个要装的就是 requests 库。这台机器在隔离的办公网段,访问不了公网,直接 pip install requests 连超时都等不到就断了。如果你也正好卡在 CentOS 7 离线安装 requests 库这一步,这篇实践记录可以让你少走几条弯路。整篇文章按照我从接需求、准备离线包、到在目标机器安装验证的完整流程来写,适合需要在内网无网络环境部署 Python 依赖的开发者和运维同学参考。

1. 这个需求的典型场景与三种方案取舍

1.1 先搞清楚:这台机器到底缺什么

不扯原理,先说需求。requests 是 Python 里最常用的 HTTP 客户端库,写一个 get/post 请求、处理 Cookie、管理重定向,比标准库 urllib 顺手太多。业务方要它,无非是要写接口调用脚本或者对接内部 API。

但这个需求不是机器上装一个文件就完事的。requests 自己有依赖:底层网络库 urllib3、证书库 certifi、国际化域名处理库 idna、字符集探测库 charset-normalizer(旧版本用 chardet)。简单说,它是一个四层以上依赖的小生态,离线环境下少一个包,import requests 就可能直接失败。目标机器虽然没法访问公网,但系统自带的 Python 2.7 是有的,这只是第一关:CentOS 7 默认 Python 2.7.5,能不能装、装哪个版本,全看默认解释器和业务要求的兼容情况。

如果机器上已经装了 python3,那情况会宽裕很多。如果没有,你首先要决定的是给系统补一个 Python 3 环境,还是在现有 Python 2.7 上退而求其次。这一步决定后面所有离线包版本的选型,千万别上来就下载最新版 requests,否则大概率白折腾一趟。

1.2 三种离线安装路线对比

第一反应基本都是本地 yum 源。内网如果配了自己的软件仓库,直接yum install python-requests确实能自动处理依赖,但它有一个非常现实的限制:CentOS 7 的默认仓库里 python-requests 版本停留在 2.6.0,是十几年前的版本,很多新特性和漏洞修复都没有。业务脚本如果只是向内部 API 发几个请求,问题不大;但如果要用到新版 requests 的某些参数或者统一依赖版本,yum 路线基本帮不上忙。

第二反应是自建内网 PyPI 镜像。这确实是团队级方案里最正规的一种,内网放一个镜像源,开发机配置好 pip 源地址,装包体验和公网几乎没有区别。但其成本在于基础设施维护,如果只是给一两台机器装一个库而专门搭镜像,投入产出比太低。

第三就是 pip + wheel 离线包。在一台能访问公网的机器上用 pip download 把所有相关包下载下来,拷到内网机器,再用--no-index --find-links指向本地目录安装。版本完全可控,依赖树由 pip 自动解析,后续其他机器还能复用同一套离线目录。我最终选的就是这个方案。

1.3 为什么我最终选了 pip + wheel 路线

简单算一笔账:yum 方案快但版本被锁死,不能满足业务方对 requests 新版本的要求;内网镜像方案好但要长期维护,为两个脚本建一个镜像多少有点杀鸡用牛刀;pip + wheel 只需要一次下载,后续所有机器复用这一套目录,边际成本几乎为零。

pip 对依赖树解析能力很强,下载时自动递归,到达内网后安装时也会自动在当前目录里查找依赖。哪怕我不小心漏了一个包,pip 会直接告诉我缺哪个,排查起来非常直观。所以这篇就以 pip + wheel 为主线往下写。实际跑完之后,我的判断是这条路最适合大多数内网单机部署场景。

2. 动手前必须确认的四件事

2.1 目标 Python 版本决定了装哪个 requests

你可以先执行这三条命令,把解释器情况摸清楚:

python -V python2 -V python3 -V

CentOS 7 默认/usr/bin/python是 2.7.5。如果业务脚本和系统脚本共用 Python 2.7,那么注意:requests 2.31.0 只支持 Python 3.7 及以上,在 Python 2.7 上跑会直接报 SyntaxError,不是简单的兼容性警告,是解释器根本不认识新语法。

近一点的版本里,requests 2.27.1 是最后一个支持 Python 2.7 的版本,同时也支持 Python 3.5 和 3.6。如果你的机器里有 python3,并且版本在 3.7 以上,那选择空间就大了,可以放心用最新的 requests 2.31.0。

我给一个比较直接的版本对照表,遇到这类问题第一眼就查它:

目标机器 Python 版本可用的 requests 版本
Python 2.7requests==2.27.1
Python 3.5 / 3.6requests==2.27.1
Python 3.7 及以上requests==2.31.0(当前最新)

这个对照是排查问题最快的工具。我这次拿到的机器是 python3.8,所以主线用 requests==2.31.0。如果读者你是 Python 2.7 环境,别灰心,后面我会有单独一节讲怎么准备旧版本依赖。

2.2 离线机器上有没有 pip,pip 怎么来

很多 CentOS 7 最小化安装机器上是没有 pip 的。这里要分两种情况处理。

第一种是有 python3 但没 pip。一般可以尝试:

python3 -m ensurepip --user

前提是 Python 在编译时带了 ensurepip 模块。只要这一条能跑通,pip 就会装进用户目录,后面所有安装命令都加上python3 -m pip前缀来调用,避免和系统 pip 混淆。

第二种是 Python 2.7.5 场景,CentOS 7 自带的这个版本比较老,没有 ensurepip。这时候必须先离线装 pip。最稳的做法是在联网机器上把 pip、setuptools、wheel 三个包一起下载好:

pip download pip==20.3.4 setuptools==44.1.1 wheel==0.37.1 -d /tmp/bootstrap/

然后把整个目录拷进内网,目标机器上执行:

python -m pip install --no-index --find-links=/tmp/bootstrap pip==20.3.4 setuptools==44.1.1 wheel==0.37.1

注意一个非常容易踩的隐藏细节:Python 2.7 环境最高只支持 pip 20.3.4 和 setuptools 44.1.1,再新的版本就不支持 Python 2.7 了。很多人在这一步用 pip 最新版安装,结果提示“Requires Python >= 3.7”,整条路直接卡死。

2.3 requests 的依赖树:脱机之前先摊开看

把 requests 当成一个黑盒直接装,容易在离线环境里反复失败。我先把依赖关系摊开:

  • urllib3:底层 HTTP 连接池、重试、证书校验,requests 的传输核心。
  • certifi:Mozilla 的 CA 证书包,requests 用它来做 HTTPS 校验。
  • idna:国际化域名编码,比如非 ASCII 域名。
  • charset-normalizer:响应字符集探测,requests 2.28 之后它是默认选项,早期版本常见依赖是 chardet。

这些包基本都是 requests 的直接依赖,pip 在下载时会一次性递归解析。如果你需要写一个锁文件,我通常会这样准备 requirements.txt:

cat > requirements.txt <<'EOF' requests==2.31.0 certifi==2024.2.2 charset-normalizer==3.3.2 idna==3.4 urllib3==1.26.18 EOF

锁到具体版本有两个好处:第一是离线包固定,不会因为某个小版本更新导致现场部署内容不一致;第二是日后要审计“这台机器上到底装了什么”时,文件就是现成的记录。注意这个锁文件适用于 Python 3.7+,如果目标环境是 Python 2.7,要换成 requests==2.27.1、chardet==4.0.0、urllib3==1.26.15、idna==2.10、certifi==2020.12.5 这一套组合。

2.4 编译与 SSL:两个容易被忽略的系统依赖

先泼一盆冷水:如果你的离线包目录里出现的是 tar.gz 而不是 whl,那安装时大概率会现场编译。目标机器要是没装 gcc、python-devel,编译直接报错。所以准备包的时候,我尽量只用 wheel 包,把编译这个不确定因素挡在门外。具体做法后面下载命令里会带--only-binary=:all:,这个参数的意思就是“只要二进制 wheel,不要源码包”。

SSL 是另一个坑。requests 发起 HTTPS 请求时,底层依赖 Python 的 ssl 模块和系统 OpenSSL。CentOS 7 的 OpenSSL 版本是 1.0.2k,正常情况下够用。但如果你是用旧 Python 源码包自己编译的,编译时没配--with-ssl,或者系统里缺少 openssl-devel,那么 import requests 没问题,真正发 HTTPS 请求时会报ssl module is not available。

我在目标机器上排查第一步就是:

python3 -c "import ssl; print(ssl.OPENSSL_VERSION)" python3 -c "import requests; print(requests.__version__)"

前者能提前暴露编译层面的问题,比等到调用接口时再报错要省事得多。ssl 模块是整个 HTTPS 链路的门槛,离线环境下出问题最麻烦,因为它和 wheel 包无关,属于解释器编译层面的问题。

3. 在联网机器上把依赖包全部拉齐

3.1 标准做法:pip download 一次性递归下载

联网机器上,先建一个干净目录:

mkdir -p /tmp/offline_pkgs cd /tmp/offline_pkgs pip download requests==2.31.0 -d ./

这条命令会自动解析整棵依赖树,把 requests、urllib3、certifi、idna、charset-normalizer 全部拉到当前目录。下载完成后执行 ls,通常会看到一批 py3-none-any.whl 和个别带平台后缀的 wheel。

这里我建议顺手加一个参数:

pip download requests==2.31.0 -d ./ --only-binary=:all:

含义是只下载二进制 wheel 包,不下载 tar.gz 源码包,确保后面安装不需要编译器。这一步能省掉后续在离线机器上碰见 gcc 报错的最大概率。

下载时尽量用和目标机器同版本 Python 的环境。比如目标机是 Python 3.8,联网机器也最好用 Python 3.8 来做下载。如果做不到完全一致,就参考下一节的跨平台下载方式。

3.2 平台不一致怎么下载:--platform 与 --only-binary

我这次比较幸运,联网机就是 Linux x86_64。但实际中很常见的尴尬是:手上只有一台 Windows 或 macOS 机器,要帮内网 Linux 机下载。这时候 pip download 默认会下载当前平台的 wheel,拷过去会发现根本装不上,报错提示“is not a supported wheel on this platform”。

正确姿势是显式指定目标平台。假设目标机器是 CentOS 7 x86_64,Python 3.8:

pip download requests==2.31.0 \ --dest /tmp/offline_pkgs \ --only-binary=:all: \ --platform manylinux2014_x86_64 \ --python-version 3.8 \ --implementation cp \ --abi cp38

这里解释一下几个参数的来龙去脉:

  • --platform manylinux2014_x86_64:指定 Linux 平台标签。CentOS 7 的 glibc 是 2.17,manylinux1、manylinux2010、manylinux2014 都能兼容,这里选 manylinux2014 覆盖面最广。
  • --python-version 3.8:指定目标机的 Python 主次版本。
  • --implementation cp:表示 CPython 实现。
  • --abi cp38:对应 CPython 3.8 的 ABI 标记。

这套参数配合--only-binary=:all:才能生效。如果你的 pip 版本比较旧,可能识别不了部分参数,建议联网机器上的 pip 先升到 20.3 以上再执行。

3.3 给 Python 2.7 环境准备旧版本依赖

如果目标机器是纯 Python 2.7,就不能套用新版本锁文件了。最稳的做法是在联网机器上用 Python 2.7 环境执行下载,因为 pip 会自动根据当前解释器选择兼容的依赖版本:

pip download requests==2.27.1 -d ./ --only-binary=:all:

如果你的联网机是 Python 3,想给 Python 2.7 目标机准备离线包,就要小心了。直接在 Python 3 环境里pip download requests==2.27.1,pip 会解析出一套适配 Python 3 的依赖组合,很可能包含 idna 3.x 这种不再支持 Python 2.7 的版本。这时候必须手动锁定旧依赖版本组合:

pip download requests==2.27.1 certifi==2020.12.5 chardet==4.0.0 idna==2.10 urllib3==1.26.15 \ -d /tmp/offline_pkgs --only-binary=:all:

这套组合我实测过是能在 Python 2.7 上正常 import 和发请求的。另外再强调一次,目标机上的 pip 建议用 20.3.4,这是最后支持 Python 2.7 的 pip 版本。

3.4 打包、校验与拷贝到内网

下载完成后,习惯先做一次目录清单确认:

ls -l /tmp/offline_pkgs/ sha256sum /tmp/offline_pkgs/* | tee /tmp/sha256sums.txt

把离线目录打成 tar 包:

tar czf requests_offline.tgz -C /tmp offline_pkgs

再通过运维交接的拷贝通道传进内网目标机。拷进去之后,解压前先比对 sha256,确认文件没有被中途改动或损坏。这一步在安全要求比较高的内网里尤其重要,别为了省三十秒把校验省掉。

4. 离线机器上的安装与验证

4.1 安置目录与准备工作

目标机器上我习惯把离线包放在 /opt/offline_pkgs,权限统一 755,避免不同用户安装时出现目录不可写的问题:

sudo mkdir -p /opt/offline_pkgs sudo tar xzf requests_offline.tgz -C /opt/offline_pkgs

然后确认目录里是不是全是 .whl 文件。如果发现混进了 tar.gz 源码包,而机器上又没有 gcc,我建议回到联网机器,用--only-binary=:all:重新下载,而不是试图在离线机器上现场装编译器。现场装编译器会引入新的依赖树,一个问题拖出三个问题,不划算。

4.2 核心命令:--no-index 与 --find-links

装之前的检查做完,安装本身就一条命令:

pip install --no-index --find-links=/opt/offline_pkgs requests==2.31.0

两个关键参数必须说清楚:

  • --no-index明确告诉 pip:不要从任何配置的索引源拉取软件包。这是离线安装的灵魂参数,少了它 pip 一定会尝试访问 PyPI。
  • --find-links=/opt/offline_pkgs告诉 pip 到这个本地目录里找候选包。pip 会把该目录下的 wheel 当作唯一的可用仓库。

如果之前写了 requirements.txt,也可以一步到位:

pip install --no-index --find-links=/opt/offline_pkgs -r requirements.txt

安装过程中 pip 会自动在离线目录里按依赖顺序解析,先装依赖后装 requests。遇到缺包,它会很明确地打印出 “Could not find a version that satisfies the requirement xxx”,一眼就能看出少了哪个。

4.3 安装后的基本验证

装完马上验证,不要等到业务上线才发现问题:

python3 -c "import requests; print('requests', requests.__version__)"

import 成功说明纯 Python 包层面的依赖都齐了,但还不足以说明全部正常,再发一个真实内网 HTTP 请求:

python3 - <<'EOF' import requests resp = requests.get('http://10.10.x.x/health', timeout=5) print(resp.status_code) EOF

HTTP 请求能返回状态码,说明 urllib3、certifi、charset-normalizer 这些底层依赖都工作正常。这一步我会放在安装流程里形成习惯,一定要跑通一次业务测通再收工,否则交付出去的机器随时可能掉链子。

4.4 另一种可用的安装姿势:逐个安装 wheel

偶尔有些环境下--find-links目录解析不了,比如目录里放了不相关的包干扰判断,可以退而求其次,把 wheel 文件直接传给 pip:

pip install --no-index /opt/offline_pkgs/*.whl

要注意:目录里有多个 wheel 时,pip 能自动解析依赖,但通配符批量安装出现版本冲突的概率比--find-links高。我一般只在排查问题时这么干,常规操作还是优先用--find-links。

5. 我在这台机器上踩过的典型坑

5.1 高频问题速查表

离线安装 requests 看着简单,实际踩坑率一点都不低。我把遇到过的和同行聊过的问题汇总成一张表,方便直接对照:

报错或现象可能原因处理方法
Could not find a version that satisfies the requirement requests本地目录没有对应包,或目录路径给错检查 --find-links 路径;确认目录里确有 whl
No matching distribution found包版本与 Python 或平台不匹配检查下载时的 --platform 和 --python-version 参数
is not a supported wheel on this platform下载到了其他系统或架构的 wheel用 manylinux2014_x86_64 等平台标签重新下载
SyntaxError: invalid syntaxPython 2.7 装了新版本 requests换 requests==2.27.1 及配套旧依赖
安装时开始调用 gcc 编译下载的是 tar.gz 源码包用 --only-binary=:all: 重下,只保留 wheel
ModuleNotFoundError: urllib3依赖没下载全,或安装时漏了依赖用 pip download 递归下载,不要加 --no-deps
ssl module is not availablePython 编译时没带 ssl 模块系统装 openssl-devel,重新编译 Python 或换带 SSL 的解释器
No module named pip目标机器没有 pip先离线安装 pip、setuptools、wheel 三件套

5.2 案例复盘一:Python 2.7 硬装新版本 requests

我此前帮某业务团队处理过同一类问题。他们在内网机器上直接把 requests 2.31.0 的 wheel 拿过去装,pip 提示安装成功,但一执行 import requests 就报 SyntaxError。后来翻代码才发现,requests 2.31 源码里用了 f-string,Python 2.7 根本不认识这种语法。最后我把整套依赖换成 requests==2.27.1 加配套旧依赖,导入和请求都正常了。

所以版本匹配不是“能装就行”,而是“解释器认不认”的问题。pip 只看安装元数据,解释器才管语法兼容。安装成功不代表可用,这个认知要在离线部署之前就建立起来。

5.3 案例复盘二:导入时报 ssl module is not available

另一个常见场景是:requests 能 import,但是一旦访问 HTTPS 接口就抛错。一看是 Python 解释器自己编译时没有 ssl 模块。这种情况你装再多 wheel 也没用,因为 requests 只会调用解释器暴露的能力。

处理办法是在离线机器上补齐 openssl-devel、python-devel 后重新编译 Python,或者换用系统自带的带 ssl 的解释器。对 CentOS 7 而言,我倾向于直接用系统 python3 或通过内网仓库安装的 python3,因为这些发行版包在编译时已经把 ssl 选项补全了。自己源码编译的 Python 是离线环境里最大的变量,能用发行版包就不要自己编译。

5.4 案例复盘三:下载了一堆 Mac 平台的包

还有一次拿到一台 macOS 机器,帮内网 Linux 下载离线包。当时没留意平台参数,pip download 把一堆 macosx_x86_64 的 wheel 拉到目录里,拷到 CentOS 7 安装时全部提示 “is not a supported wheel on this platform”。重新用--platform manylinux2014_x86_64 --python-version 3.8 --abi cp38下载一次才解决。

从那以后我下载前必看目标系统的架构和 Python 版本,用uname -m确认架构,用python3 -V确认版本,再决定下载参数。离线安装没有太多尝试空间,错了就得从头再来,前置确认远比现场试错重要。

写到这里,这套流程基本就完整了。我在实际部署里最深的体会是:离线安装没有技术运气可言,每一步都是一次验证。你在联网机器上少花十分钟,到了内网可能要多花一晚上;相反,把依赖树、平台标签、SSL 模块这些前置条件都核对清楚,真正的安装命令只要两秒。另外建议把 /opt/offline_pkgs 这套离线包归档起来,版本号写进文件名,下次再遇到新机器直接复制过去用,省下的时间就是实打实的收益。如果后续要升级 requests,只需在联网机器上重跑一次 pip download,替换目录里的包即可。按这个套路走熟之后,内网装依赖就不再是让人头疼的事了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询