用pip生成requirements.txt的四种主流方式与避坑指南

发布时间:2026/10/12 8:10:37
用pip生成requirements.txt的四种主流方式与避坑指南
你有没有过这种经历项目代码明明在自己电脑上跑得好好的发给同事或者传到服务器上一运行就是满屏的ModuleNotFoundError。然后你打开对方的环境一看——缺了十几个第三方库版本还不一致。追根溯源就是打代码包的时候漏了一件事没有把依赖清单一起交付。这份清单在 Python 项目里就是requirements.txt而生成它最顺手、最常用的工具就是每个 Python 开发者天天都在用的pip。这篇文章我会从一个实际项目的角度把pip生成requirements.txt的几种主流方式都捋一遍包括pip freeze、pipreqs、pip-tools这些工具怎么选、怎么用以及我在真实项目中踩过的坑Windows 下pip无法识别、系统 Python 报externally-managed-environment、生成出来的清单又臭又长不好维护…… 新手朋友可以直接照抄操作步骤有经验的朋友也可以重点看第三、四章的工具选型和排查思路。1. 为什么每个 Python 项目都需要一份 requirements.txt1.1 从项目复现的痛点说起我刚开始写 Python 的时候也懒得管依赖觉得“代码能跑就行”。直到有一次把一个爬虫项目发给朋友跑他那边报错的点跟代码逻辑完全没关系全卡在第三方库上requests没装、lxml版本太老、pandas装了 2.x 但代码里用的接口在 1.x 里才有。我那天远程指导他装库装了快两个小时比我自己重写一遍还累。后来我才意识到所谓“项目可复现”不只是代码本身对还包括它的运行环境是确定的。你把.py文件递给别人别人光是猜测需要哪几个第三方包就得费半天劲就算知道了包名版本不锁定对方也可能装到不一样的行为。requirements.txt就是用来一次性说清楚“我这个项目依赖哪些包、分别是哪个版本”的声明文件。它解决的是三个层面的问题新人接手项目时不用问“还需要装什么”部署到服务器或容器时能精确还原开发环境以及回滚时能快速切回旧版依赖组合。说白了这就是一份“环境快照清单”。1.2 requirements.txt 到底记录了什么很多人以为requirements.txt就是个简单的包名列表其实它的语法有自己的一套规则pip解析时完全按照 PEP 508 规范来读取。最常见的格式是精确版本锁定requests2.31.0最低版本约束requests2.20区间约束requests2.20,3.0兼容版本约束requests~2.31等价于2.31,3.0还能写注释用#开头以及通过-r other.txt引入其他依赖文件通过-e .安装当前项目本身。我见过不少大型项目把requirements拆成base.txt、dev.txt、prod.txt再用-r串起来这也是这套语法灵活的地方。pip读取这份文件时会按照文件里的约束去 PyPIPython 官方包索引或你配置的镜像源拉取对应版本。所以requirements.txt不光是“给人看的”更是“给 pip 执行的脚本”。理解了这一点你就知道为什么它的格式规范这么重要格式一乱pip install -r就会直接报错。2. 用 pip 生成 requirements.txt 的四种主流方式2.1 pip freeze一条命令快速导出如果只记一个命令那就是pip freeze。它会把当前 Python 环境里通过pip安装的所有第三方包连同精确版本号一次性列出来再加个重定向符就能写入文件pip freeze requirements.txt执行完后打开requirements.txt里面就是类似这样的内容certifi2024.2.2 charset-normalizer3.3.2 idna3.6 requests2.31.0 urllib32.2.1pip freeze最大的优势是快、全覆盖不遗漏任何通过pip装进去的东西。但我必须提醒你它导出的是“环境中所有包”不是“项目真正用到的包”。如果你的环境是全局 Python里面可能有你半年前装来玩儿的flask、测试用的pytest甚至和你项目毫无关系的jupyter这些全都会被写进requirements.txt。所以我的习惯是pip freeze只在虚拟环境里用。配合venv或conda创建的干净环境导出结果才等于项目真实依赖。2.2 pipreqs只扫描实际用到的包pip freeze的问题是“给环境做快照”而更多时候我们想要的是“给项目代码做清单”。这时候就该用pipreqs。pipreqs的原理是扫描你项目目录里所有.py文件提取其中的import语句然后去 PyPI 反查包名生成一份只包含“代码里真正被引用到”的依赖清单。它不属于pip自带工具需要单独安装pip install pipreqs进入项目根目录后执行pipreqs . --encodingutf8 --force注意我加的两个参数--encodingutf8是防止 Windows 下读取代码文件时出现中文注释导致的编码报错--force是覆盖已存在的requirements.txt。生成出来的清单一般非常干净比如你代码里只 import 了requests、pandas、jieba它就不会把urllib3这种传递依赖写进去。pipreqs也不是万能的。它对动态导入importlib.import_module这种、字符串形式的 import、写在配置文件里的包名会漏检对包名和模块名不一致的库也可能给出错误结果。比如pillow的模块名是PILpipreqs在处理时需要靠它内置的映射表来判断万一没命中就得手动补一下。2.3 pip-tools拿捏版本范围的优雅方案pipreqs解决了“清单不干净”的问题但和团队协作、长期维护结合起来看还有一个痛点直接锁定精确版本虽然可靠但升级依赖时要全局改一遍。而且pip freeze生成的清单里充满传递依赖升级一个小版本可能引发连锁反应。如果你想用更省心的方式管理依赖版本我推荐pip-tools。它的工作流分两步你维护一个requirements.in文件里面只写“顶层依赖”和宽松的版本范围甚至不写版本号requests pandas2.0 flask用pip-compile编译生成锁定了精确版本的requirements.txtpip-compile requirements.inpip-compile会自动解析依赖关系把传递依赖也写进最终的requirements.txt并在文件头部标注生成时间和使用的工具版本。以后想升级某个包只需要改requirements.in再重新跑一次pip-compile就行了。这套流程对单个项目可能显得重但用到多个项目长期维护、或者 CI/CD 需要可重复构建的场景收益非常明显。我在维护一个内部工具库的时候就是用pip-tools每次发布前重新编译一份锁文件回滚时直接切旧 lock 文件基本没出过依赖事故。2.4 手工精修把 requirements.txt 变成一份可读文档工具生成只是第一步真正专业项目的requirements.txt通常还会经过手工整理。我见过最好的做法是把依赖分组并加注释# 核心运行时依赖 requests2.31.0 flask3.0.3 redis5.0.3 # 数据处理 pandas2.0,2.2 numpy~1.26 # 测试 pytest8.0 pytest-cov4.0注意这里的版本写法核心依赖尽量用精确锁定保证生产环境稳定对有一定兼容余量的库用范围约束比如2.0,2.2既避免装到太老版本也防止大版本升级带来的破坏。另外还有一个小技巧如果项目需要支持不同的 Python 版本可以使用环境标记语法例如pywin32306; sys_platform win32这样写的意思是只有 Windows 平台才安装pywin32其他平台自动跳过。这个语法是 PEP 508 的标准能力pip原生支持很多新手不知道配合跨平台项目特别好用。3. 实操从零到一完成依赖导出与复现3.1 环境准备虚拟环境与 pip 自身确认生成requirements.txt的第一步不是直接敲命令而是先确认自己身在哪个环境。前面说了全局环境下用pip freeze会把一堆无关包带进来所以强烈建议先建虚拟环境。Python 3.3 之后自带venv模块不需要额外安装。一条命令创建一条命令激活python -m venv .venvWindows 下激活.venv\Scripts\activatemacOS 或 Linux 下激活source .venv/bin/activate激活之后命令行提示符前面会出现(.venv)的前缀这表示你已经处在独立环境里了。此时检查pip是否能正常工作我习惯用python -m pip而不是直接pippython -m pip --version这里要解释一下为什么直接敲pip依赖的是系统 PATH 里能找到pip.exe或pip脚本一旦 Python 安装时没把Scripts目录加进 PATHWindows 下就会报“无法将 pip 项识别为 cmdlet、函数、脚本文件或可运行程序的名称”。而python -m pip是“让 Python 解释器以模块方式运行 pip”无论 PATH 怎么配只要python本身能跑这条命令就不会出错。3.2 生成完整 requirements.txt 并验证环境确认无误后有两种典型的生成场景。如果你拿到的是一个从没装过依赖的新环境准备安装项目所需的包pip install requests flask pandas装完后一键导出pip freeze requirements.txt如果你想确认当前环境里已有的包之间有没有版本冲突可以顺手跑一下pip checkpip check会扫描所有已安装包的依赖关系找出互相冲突的组合。输出为空表示一切正常如果有问题它会明确告诉你哪个包要求什么版本、但当前环境里装的是哪个版本。生成之后一定不要急着收工打开requirements.txt看一眼重点确认几件事是否包含项目真正用到的核心包版本号是否都完整不该出现pkg这种没有版本号的裸行有没有明显与当前平台绑死的包比如pywin32、pymssql这类有没有包含一些大型 GUI 库却和项目无关这是全局环境导出的通病。我见过有人直接把全局环境的pip freeze结果发给别人里面带了matplotlib、notebook、pyside6等一堆重库安装起来又慢又容易冲突。这种文件与其叫requirements.txt不如叫环境备份。3.3 新环境完整复现pip install -r 实战有了requirements.txt在另一台机器或新环境里复现项目就变成一条命令的事pip install -r requirements.txt但在实际执行时有几个细节决定了体验差异。首先是国内下载太慢的问题。如果你直接走官方 PyPI几百 MB 的依赖组合能等到怀疑人生。我强烈建议配一个国内镜像源比如清华 PyPI 镜像pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple或者一劳永逸把镜像配置写进 pip 的全局配置文件。Windows 下在用户目录创建pip.iniLinux/macOS 下创建pip.conf[global] index-url https://pypi.tuna.tsinghua.edu.cn/simple配置之后之后所有pip install都不用再带-i参数。镜像源可以理解为一个 PyPI 在国内的缓存仓库里面的包内容跟官方一致只是下载速度快几个量级。其次pip install -r遇到版本冲突时会中断安装并且提示具体是哪两个包在争抢依赖。此时最常见的处理方式是升级依赖解析策略。pip 默认使用--resolverfactory或旧版兼容策略如果你遇到奇怪的解析失败可以尝试pip install --resolverbacktracking -r requirements.txtbacktracking解析器会在依赖约束冲突时主动回溯尝试不同版本组合是 pip 22.0 之后的推荐默认策略能解决很多“明明都满足单个约束但组合安装失败”的诡异问题。4. 常见问题与排查技巧实录4.1 pip 未识别、未安装的情况热词里出现频率最高的报错是pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称这是 Windows 下最常见的 PATH 配置问题。解决思路很简单不要直接pip改用python -m pip。但更深层的问题是这个环境压根没有 pip。Python 3.4 之后的 Linux 发行版里系统 Python 默认不带 pipWindows 上如果在安装 Python 时没有勾选 “pip” 组件也会出现 pip 缺失。拿到这种环境的正确姿势是先确保python命令能用然后立刻安装 pippython -m ensurepip --upgradeensurepip是 Python 官方提供的“最小化 pip 安装器”只要解释器存在就能执行。装完之后再用python -m pip -V验证之后所有 pip 操作都建议用python -m pip这种写法来规避 PATH 问题。4.2 externally-managed-environment 报错最近很多人在 Linux 系统上遇到这个报错error: externally-managed-environment这是 Python 3.11 之后引入的 PEP 668 保护机制系统 Python 环境受操作系统的包管理器如 apt、dnf管理不允许你直接用 pip 往里装包防止破坏系统级 Python 环境。报错信息非常直白这段环境不是给你随便折腾的。我看到不少教程让人加--break-system-packages强装这种做法只能应急不推荐长期依赖。正确解法是一开始就建虚拟环境python -m venv .venv source .venv/bin/activate虚拟环境是独立目录PEP 668 的保护只针对系统 Python不会干扰venv。相信我这件事上没有捷径老老实实用虚拟环境可以避开无数后续问题。4.3 提示 must give at least one requirement另一个高频报错是ERROR: You must give at least one requirement to install (see pip help install)这个基本发生在执行pip install -r requirements.txt时原因有几种requirements.txt文件不存在、文件名写错、或者文件内容是空的。最容易忽略的是从 Windows 复制文件名到 Linux 时带了不可见字符或者文件编码是带 BOM 的 UTF-8导致 pip 把第一行解析成空参数。排查思路就三步先ls确认文件存在再用cat -A requirements.txt检查有没有乱码字符最后用python -m pip install -r requirements.txt执行。如果文件为空回到生成步骤pip freeze重新导一次。4.4 依赖版本混乱与复杂项目的整理技巧requirements.txt最常见的坑是生成时环境状态正常但过两个月再装发现某个包的间接依赖升级后引入不兼容行为。这类问题用肉眼很难发现我推荐两个工具配合排查pipdeptree用来查看依赖树pip install pipdeptree pipdeptree它会以树状结构展示当前环境里所有包的依赖关系。看输出你能立刻明白为什么opencv-python装了之后numpy被升级原来是opencv-python对numpy有最低版本要求。还有一个实用命令是pip list --outdated列出所有可升级的包。结合pipdeptree你就可以判断哪些包值得升级、哪些包因为传递依赖绑定了版本不能动。如果依赖本身就很乱另一个非常实用的做法是删掉requirements.txt在干净环境里从项目源码重新生成。开发到一定阶段后用pipreqs扫一遍真实 import陈旧依赖一次性清干净。这是我定期对项目做“依赖大扫除”的固定动作实测下来每次都能减少 10%~20% 的无效依赖。最后总结一下我在实际项目中的固定流程初始化仓库时建好虚拟环境依赖管理工具优先用pip-tools平时开发不管requirements.txt只维护requirements.in要发布或分享时跑一次pip-compile生成锁版本如果是快速验证的小脚本就直接pip freeze requirements.txt完事。这套流程兼顾了效率和可维护性踩过几次坑之后你会发现一份干净的依赖清单比代码本身更值得认真对待。