说实话,干 Python 开发这几年,requirements.txt这个东西几乎天天见。不管你是刚入门写爬虫、做数据分析,还是折腾机器学习项目,迟早都会遇到这个文件。它的作用说白了就一句话:把项目依赖的第三方包及版本记录下来,让别人(或者几个月后的你自己)一条命令就能把环境跑起来。
但就是这么个基础操作,我在各种技术群里看到的问题却一点也不基础。有人装了几天装不上,有人报“pip 不是内部或外部命令”,有人用清华镜像装到一半提示证书错误,还有人辛辛苦苦跑完pip freeze,结果生成的 requirements.txt 有一堆用不上的包。这篇文章我不打算讲太多虚的,直接把我平时的工作流程、踩过的坑、排查思路全部摊开讲清楚,尤其是“如何生成 requirements.txt”和“如何用 pip 安装 requirements.txt”,这两件事儿从头到尾过一遍。
1. 先搞明白 requirements.txt 到底解决什么问题
1.1 为什么每个 Python 项目都绕不开它
很多新手不理解,我本地代码不是跑得好好的吗?为什么要搞一个 requirements.txt 出来?这里我用一个特别生活化的例子解释一下。
你写了 100 行 Python 代码,用到了一堆第三方库,比如requests、pandas、openpyxl。代码本身没问题,但你换一台电脑、或者发给同事跑的时候,对方机器上大概率没有这些库。如果没有 requirements.txt,你根本说不清楚自己到底装过哪些包、分别是什么版本。人工去回忆、去pip list一个个人工核对,纯属浪费时间,而且极易漏掉间接依赖。
requirements.txt 的作用就是“环境快照”。它把项目依赖的包名、版本、来源写清楚,别人拿到项目之后一句话还原环境:
pip install -r requirements.txt这句话等价于把文件里记录的每个包逐个安装,但不需要你手动执行几十次。对于线上部署、开源项目发布、团队协作、换电脑重配环境这些场景,它都是必需品。
1.2 requirements.txt 的格式与版本控制规则
先看一个典型文件内容,建立直观印象:
flask==2.3.3 requests>=2.31.0,<3.0.0 pandas~=2.1.0 opencv-python -e git+https://github.com/xxx/project.git#egg=project逐行拆开解释:
flask==2.3.3:精确锁定版本。这是最推荐的生产环境写法,保证任何人安装的结果完全一致。requests>=2.31.0,<3.0.0:版本范围,允许 pip 在满足条件的情况下自动选择最新版本。灵活,但有一定不确定性。pandas~=2.1.0:兼容版本号。~=2.1.0等价于>=2.1.0,==2.1.*,也就是允许 2.1.x 内的补丁版本升级,但不跨小版本。opencv-python:不写版本号,表示装最新版。-e git+...:直接安装 Git 仓库里的包,常用于私有库或未发布的源码项目。
另外文件里还支持空行和#注释,比如:
# 这是 Web 框架 flask==2.3.3我不建议在 requirements.txt 里写太多注释,因为这个东西最终是机器读的,人工阅读的场景不多。但适当地按用途分块,后期维护起来确实省心。
这里还有个细节必须说:pip 在解析时不是简单“从上到下逐行安装”,而是先收集所有包的信息,再做依赖解析。所以文件里两个包写 A 依赖 B、B 又反过来依赖 A 的这种循环情况,以及不同行之间版本约束冲突,pip 都会在安装前报错。这也是很多人遇到ERROR: Cannot install xxx的根源之一,后面我会专门讲排查方法。
2. 如何生成 requirements.txt:三种方案按需选择
2.1 最省事的方案:pip freeze
如果你只是想备份当前环境里所有已安装的包,那直接用 pip 自带命令即可:
pip freeze > requirements.txt简单、快捷、零依赖。执行完后,当前环境里的所有包以及它们各自的版本号都会按包名==版本号的格式写入文件。
但我要提醒你,pip freeze有个明显的坑:它会把环境里所有的包都导出来,包括那些和当前项目无关的包。比如你的 Windows 机器全局环境里装了十来个数据分析库,你只是拿它跑一个 Flask 小 Demo,pip freeze也会把这十来个库全塞进去。别人拿到你的 requirements.txt 一装,白白多装一堆用不上的包,还可能因为某些包版本冲突直接安装失败。
所以我的建议是:pip freeze只能在虚拟环境里放心用,或者用于“整体备份环境”的场景。全局环境里生成的 requirements.txt,慎用。
如果你确实只有一个项目并希望精准导出,请继续往下看。
2.2 按项目实际导入生成:pipreqs
pipreqs 的思路完全不同。它不去扫描环境里有什么,而是扫描你项目的源码,找出所有import语句,再反查这些包在当前环境中的对应版本,最终生成一个只包含真实依赖的文件。
用法如下:
pip install pipreqs # 在项目根目录执行 pipreqs . --encoding=utf8 --force参数说明:
.:扫描当前目录的代码。--encoding=utf8:如果你的源码里有中文注释,建议加上,避免编码错误。--force:目标位置已有 requirements.txt 时强制覆盖。
我给一个实际见过的例子。某次接手同事的爬虫项目,他用pip freeze导出了 86 个包,里面甚至有jupyter、matplotlib这种明显不属于项目本身的玩意。我用 pipreqs 重新生成了一遍,只剩 9 个核心依赖,部署时安装速度快了一大截。
不过 pipreqs 也不是完美的。它对动态导入、延迟导入的支持不太好,比如代码里用了__import__或者在函数内部才 import 某些包,有可能漏掉。另外如果项目里两个模块同名,它也可能映射错包名。所以生成完之后,我建议你人工快速扫一遍,再把关键版本号补上。
2.3 锁定完整依赖树的进阶方案:pip-tools
如果你做的是库开发、或者严格要求的项目交付,pipreqs 精确到“直接依赖”还不够,你还需要把依赖的依赖也锁死。这时候用 pip-tools 是最合适的。
具体流程分三步。
第一步,创建 requirements.in 文件,里面只写直接依赖,可以有宽松的版本范围:
flask>=2.0 requests pandas第二步,安装 pip-tools 并编译:
pip install pip-tools pip-compile requirements.in第三步,生成 requirements.txt。这个文件会把每个直接依赖对应的所有间接依赖全部展开,并锁定精确版本。文件开头还会生成一行注释,提示你不该手动编辑这个文件,应该改 requirements.in 后重新编译。
pip-tools 还支持pip-sync命令,它会把当前环境调整为和 requirements.txt 完全一致,多装的包自动卸载,单环境还原终极利器。
如果你主要在维护开源项目或做需要定期重建的复杂工程,我建议认真研究一下这个工具。
3. 用 pip 安装 requirements.txt:命令、镜像与虚拟环境
3.1 一条命令装完所有依赖
生成好 requirements.txt 之后,安装就是一条命令的事:
pip install -r requirements.txt这里的-r是--requirement的简写,意思是“从指定文件中读取依赖列表并安装”。日常大家写习惯了,但确实有人不知道-r是什么意思。
执行过程中,pip 会先解析文件内容,再联网下载并安装。输出信息里如果出现Successfully installed xxx-1.0.0,说明安装完成。如果出现Requirement already satisfied: xxx,说明这个包在当前环境里已经满足要求,pip 不会重复安装。
需要说明的是,如果在 requirements.txt 里指定的是精确版本==,pip 安装时会严格遵守;如果是范围版本,pip 会自动解析出满足范围的最新版本。对于生产环境交付,我强烈建议生成文件时直接用精确版本,“能跑”和“可复现”是两码事。
如果你想在安装时顺便升级 pip 到最新版,可以单独执行:
python -m pip install --upgrade pip不建议在pip install -r requirements.txt后面直接加-U,因为-U会无视文件里的精确版本约束强制升级,很容易把本来锁定好的环境搞乱。
3.2 国内镜像源加速:不再干等超时
pip 默认从位于 pypi.org 官方源下载包。遵循仓库维护的初衷,官方源在国外服务器,网络波动大,国内用户经常遇到下载超时、卡在进度条半天不动、甚至依赖包下载到一半就断掉的情况。
解决办法就是换镜像源。国内有多个稳定可用的 PyPI 镜像,下面列几个常用的:
| 镜像名称 | PyPI 地址 |
|---|---|
| 清华 TUNA | https://pypi.tuna.tsinghua.edu.cn/simple |
| 阿里云 | https://mirrors.aliyun.com/pypi/simple/ |
| 中科大 | https://pypi.mirrors.ustc.edu.cn/simple/ |
| 腾讯云 | https://mirrors.cloud.tencent.com/pypi/simple/ |
临时指定镜像安装:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple如果镜像服务器偶尔不稳定,可以再加一个备用参数:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn不过在现代 pip 版本中,带 HTTPS 证书的镜像一般不需要--trusted-host,只有老版本或者 HTTP 地址才需要。
比起每次手打-i参数,我更推荐直接改 pip 全局配置,一劳永逸:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple执行后 pip 会把这个配置写入配置文件。不同系统对应位置不同:Linux 是~/.config/pip/pip.conf,macOS 是~/Library/Application Support/pip/pip.conf,Windows 是C:\Users\用户名\AppData\Roaming\pip\pip.ini。你也可以手工编辑这些文件,效果一样。
3.3 虚拟环境:先隔离再安装
这是我在实践中最想强调的一点。很多新手习惯把项目依赖直接装进 Python 的全局环境,一旦同时开发多个项目,就会碰到“项目 A 需要 Flask 2.3,项目 B 需要 Flask 1.0”这种互相打架的场景。
虚拟环境就是一个隔离舱,每个项目各用各的依赖,互不干扰。我推荐的工作流是:每次新建项目,先建虚拟环境,再安装依赖。
用标准库 venv 的流程:
# 创建虚拟环境,命名为 venv(名称可以自定义) python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate # 激活后,命令行提示符前面会出现 (venv) 前缀激活后执行安装:
pip install -r requirements.txt这样装出来的包全部落在 venv 目录里,不会污染系统 Python。项目完成后,删除整个 venv 目录即可,干净利落。
如果你用 Anaconda 或 Miniconda,可以用 conda 创建环境:
conda create -n myenv python=3.11 conda activate myenv pip install -r requirements.txt我个人习惯是 conda 负责管理 Python 版本和带编译的底层库,纯 Python 依赖统一交给 pip,两者配合,基本不会出大问题。
3.4 区分开发与生产环境的依赖拆分思路
项目稍微复杂一点,就不该只有一套 requirements.txt。开发时需要额外装 pytest、black 这类调试和格式化工具,生产环境完全不需要。拆成两个文件更合理:
requirements.txt # 运行项目必需的依赖 requirements-dev.txt # 开发调试用的额外依赖requirements-dev.txt 里可以引用主文件:
# requirements-dev.txt -r requirements.txt pytest==8.0.0 black==24.1.1安装开发环境依赖时执行:
pip install -r requirements-dev.txt安装生产环境依赖时执行:
pip install -r requirements.txt这样拆开的好处是生产环境尽量精简,减少攻击面,也减少不必要的安装时间。依赖越少,环境越稳。
4. 安装过程中最常见的 6 个坑及排查方法
4.1 pip 命令报错:不是内部或外部命令
Windows 上最常见,输入pip后提示:
pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。这个问题的本质是 pip 的可执行文件所在目录不在 PATH 环境变量里,系统找不到这个命令。解决办法有几个:
第一,用模块方式调用,绕开 PATH 问题:
python -m pip install -r requirements.txtpython -m pip表示以模块方式运行 pip,只要 python 本身在 PATH 里就能用。
第二,手动把 pip 对应目录加入 PATH。Windows 上一般是:
C:\Users\你的用户名\AppData\Local\Programs\Python\Python311\Scripts把路径加到系统环境变量 Path 后,重新打开命令行即可。
第三,彻底修复 pip:
python -m ensurepip --upgrade这个方法在 pip 本体损坏或缺失时很有用,它会把 pip 重新装回 Python 环境。
另外很多用 Miniconda 的朋友反馈“conda 里的 python 不能使用 pip”,这通常是因为命令行里跑的 pip 指向了另一个 Python 的 Scripts 目录。解决办法是别直接敲 pip,先执行:
python -m pip --version看看当前 pip 属于哪个 Python,确认指向是否正确,再执行安装。
4.2 下载慢、超时、SSL 证书报错
这类问题我在群里回答得最多。症状主要有三种:
- 卡在
Downloading xxx半天不动; - 报
ReadTimeoutError: HTTPSConnectionPool; - 报
Could not fetch URL ... connection problems。
90% 的情况就是网络不畅,解决办法就是换镜像源,参见前面 3.2 节。如果连了镜像还偶尔超时,可以追加超时和重试参数:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple --timeout 60 --retries 5如果遇到证书校验失败的报错,通常是你用的源是老式 HTTP 地址,或者公司网络做了 HTTPS 拦截。你可以尝试:
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com但我必须说明,--trusted-host本质是跳过主机证书验证,仅在明确知道源可信任时使用,不要在日常随便加,容易造成安全风险。
4.3 编译类依赖包安装失败
这类问题的特征最典型,安装某个包时 pip 开始编译源码:
Building wheel for xxx (setup.py) ... error正常情况下,绝大多数常用包在主流平台都有预编译的 wheel 文件,pip 直接下载安装即可,根本不需要本地编译。出现“Building wheel”说明 pip 没找到合适的预编译包,只能退回源码安装。而源码安装往往依赖系统的 C/C++ 编译器,只要环境缺了工具链,就会立刻报错。
处理思路有三个方向:
其一,确认 Python 版本。某些包的新版本只提供最高 Python 3.11 的 wheel,你如果还在用 Python 3.7,旧版本平台的预编译包可能已经下架,就会触发源码编译。尽量换用 Python 3.8、3.10、3.11 这类主流版本,遇到 wheel 缺失的概率小得多。
其二,安装编译工具链。Windows 上装 Visual Studio Build Tools,并勾选“使用 C++ 的桌面开发”组件;Linux 上装build-essential、python3-dev。
其三,给 pip 指定二进制的预编译 wheel 来源。一个典型例子是dlib,从源码编译能折腾一晚上,而通过pip install dlib-bin或者专门的 wheel 源,几分钟就完事。同理,opencv-python安装失败时,可以换成opencv-python-headless,它不含 GUI 相关依赖,在服务器环境下反而更合适。
4.4 版本冲突:ERROR: Cannot install
这个报错的完整描述通常是:
ERROR: Cannot install xxx-1.0 and yyy-2.0 because these package versions may conflict.pip 在安装前会做依赖解析,如果 requirements.txt 里的两个包互相依赖的版本无法同时满足,就会直接拒绝安装。遇到这种问题不要慌,按下面的顺序排查。
先尝试去掉文件里面太死的版本约束。比如有人写numpy==1.26.0,另一个包又要求numpy>=2.0,必然冲突。把它改成:
numpy>=1.26.0再让 pip 自动解析出一个双方都能接受的版本。
然后可以安装 pip 自带依赖检查工具跑一下现状:
pip check如果当前环境里存在依赖不满足的情况,它会明确列出来。结合报错信息,删除或调整对应包:
pip uninstall 包名如果 a 包只兼容旧版 b 包、c 包只兼容新版 b 包,而你两个都要用,那只能放弃其中一个,或者等待上游更新。这是真正的版本地狱,没有银弹。
4.5 包装到了错误的环境里
新手在 Windows 上装了三个 Python,或者同时有 Anaconda 和系统 Python,就会出现“我明明 pip install 了,但运行脚本还是找不到包”的诡异现象。其实大概率是装错了环境。
排查思路非常简单:
# 看当前 pip 对应的 Python 路径 python -m pip --version # 看当前环境的包列表 python -m pip list确认路径之后,再确认你的 IDE 或运行脚本用的解释器是不是同一个。PyCharm 默认会用项目配置的解释器,而不是系统 PATH 里的 Python,这个差异极其容易把人绕进去。
养成一个好习惯:凡是装包,都用python -m pip而不是单独敲pip,因为前者明确指出由哪个 Python 执行,几乎不会装错。
4.6 依赖删不掉、缓存导致安装失败
还有一种情况是包已经被安装,但 pip 报错说找不到、或者升级后版本不对。这往往和 pip 的本地缓存有关。pip 会把下载的包缓存到本地,下次安装时可能直接使用缓存,而缓存数据在极少数情况下会损坏,导致各种奇怪报错。
清理缓存:
pip cache purge顺便提一句,再老的版本还有pip cache info可以查看缓存占用情况,有的机器缓存几十个 G,清一清还能腾出不少磁盘空间。如果你在高可信度环境下怀疑是缓存问题,清理后再重新安装基本就能解决。
5. 一些实战中的个人体会
说了这么多,最后分享几个我自己长期形成的习惯,算是给这一整件事收个尾。
第一,所有项目从第一天就进虚拟环境,隔离意识养成了,后面能少踩一半的坑。哪怕只是写一个十几行的脚本,只要它要装第三方库,我都习惯先建一个环境。顺手的事,回报却非常高。
第二,生成 requirements.txt 这件事,我一般不在项目一开始就做。通常是功能开发告一段落、依赖趋于稳定之后,再用 pipreqs 精炼生成,然后人工补充版本号。交付前如果有条件,再配 pip-tools 锁一次完整依赖树,顺手跑一遍pip-sync验证能在干净环境里成功安装。
第三,遇到安装报错先读报错原文,别急着搜代码。pip 的报错信息其实写得很清楚,它会告诉你缺少什么、冲突在哪里。把报错贴进搜索引擎之前,先自己尝试理解前 10 行,这个习惯一旦养成,解决问题的能力会有质的提升。
最后再提醒一条:再好的工具也抵不过烂网速,国内用户老老实实配一个全局镜像源,再配合超时重试参数,我实测下来安装大型依赖(比如 torch、opencv 全家桶)的时间能缩短一半以上。趁早动手把你自己的 requirements.txt 流程理清楚,以后换电脑、发版本、拉同事入伙,都能省下大量时间。