Python依赖管理:requirements.txt生成与pip安装实战指南
说实话干 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 的格式与版本控制规则先看一个典型文件内容建立直观印象flask2.3.3 requests2.31.0,3.0.0 pandas~2.1.0 opencv-python -e githttps://github.com/xxx/project.git#eggproject逐行拆开解释flask2.3.3精确锁定版本。这是最推荐的生产环境写法保证任何人安装的结果完全一致。requests2.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 框架 flask2.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 小 Demopip freeze也会把这十来个库全塞进去。别人拿到你的 requirements.txt 一装白白多装一堆用不上的包还可能因为某些包版本冲突直接安装失败。所以我的建议是pip freeze只能在虚拟环境里放心用或者用于“整体备份环境”的场景。全局环境里生成的 requirements.txt慎用。如果你确实只有一个项目并希望精准导出请继续往下看。2.2 按项目实际导入生成pipreqspipreqs 的思路完全不同。它不去扫描环境里有什么而是扫描你项目的源码找出所有import语句再反查这些包在当前环境中的对应版本最终生成一个只包含真实依赖的文件。用法如下pip install pipreqs # 在项目根目录执行 pipreqs . --encodingutf8 --force参数说明.扫描当前目录的代码。--encodingutf8如果你的源码里有中文注释建议加上避免编码错误。--force目标位置已有 requirements.txt 时强制覆盖。我给一个实际见过的例子。某次接手同事的爬虫项目他用pip freeze导出了 86 个包里面甚至有jupyter、matplotlib这种明显不属于项目本身的玩意。我用 pipreqs 重新生成了一遍只剩 9 个核心依赖部署时安装速度快了一大截。不过 pipreqs 也不是完美的。它对动态导入、延迟导入的支持不太好比如代码里用了__import__或者在函数内部才 import 某些包有可能漏掉。另外如果项目里两个模块同名它也可能映射错包名。所以生成完之后我建议你人工快速扫一遍再把关键版本号补上。2.3 锁定完整依赖树的进阶方案pip-tools如果你做的是库开发、或者严格要求的项目交付pipreqs 精确到“直接依赖”还不够你还需要把依赖的依赖也锁死。这时候用 pip-tools 是最合适的。具体流程分三步。第一步创建 requirements.in 文件里面只写直接依赖可以有宽松的版本范围flask2.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 地址清华 TUNAhttps://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.confmacOS 是~/Library/Application Support/pip/pip.confWindows 是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 python3.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 pytest8.0.0 black24.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 后重新打开命令行即可。第三彻底修复 pippython -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 里的两个包互相依赖的版本无法同时满足就会直接拒绝安装。遇到这种问题不要慌按下面的顺序排查。先尝试去掉文件里面太死的版本约束。比如有人写numpy1.26.0另一个包又要求numpy2.0必然冲突。把它改成numpy1.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 流程理清楚以后换电脑、发版本、拉同事入伙都能省下大量时间。