有时候人不在目标服务器旁边又要装一堆 Python 依赖最常用的办法就是在本地先把 pip 包下载好拷过去离线安装。但很多人在这一步就卡住了明明本地是 Windows x64目标机器是 Linux ARM64直接pip download一堆命令跑完拷过去才发现装不上。问题出在没搞清楚 pip download 默认只按“当前解释器 当前操作系统 当前 CPU 架构”去拉包。这篇就把怎么指定 CPU 类型和 OS 类型下载 pip 包讲透包括每个参数背后的匹配规则、具体命令模板、常见报错和处理方法。1. pip download 默认行为与为什么要改平台参数1.1 默认下载的是“当前环境能装的包”pip download在绝大多数人的认知里就是pip install的下载版它会先分析当前环境的 Python 版本、操作系统、CPU 架构然后从 PyPI 上找满足这些条件的 wheel 包下载到指定目录。听起来没问题但换个目标平台就不行了。举个例子你的开发机是 Windows 10 x64安装的是 Python 3.10这时候执行pip download numpy -d ./offlinepip 会优先下载类似numpy-1.26.4-cp310-cp310-win_amd64.whl的文件。文件名里的win_amd64就是当前平台生成的 wheel 标签。如果你要把这个包拷到一台 Linux aarch64ARM64服务器上Linux 环境根本不会认win_amd64标签安装时直接提示“is not a supported wheel on this platform”。所以跨平台下载的第一步就是明确告诉 pip别猜了我就是要给另一个 CPU / OS 下载包。核心参数就是--platform、--python-version、--implementation、--abi再用--only-binary:all:确保只拉预编译的 wheel不拉源码包。1.2 pip 是怎么用文件名匹配平台的要理解怎么指定参数先要明白 pip 的匹配逻辑。PyPI 上的 Python 包有两种常见形态wheel.whl和源码发布包.tar.gz/.zip。wheel 文件名本身就是规范化的兼容性标签格式是{distribution}-{version}-{python tag}-{abi tag}-{platform tag}.whl例如pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl拆开看cp311Python 实现 版本CPython 3.11。第二个cp311ABI 标签表示二进制接口适用于 CPython 3.11。manylinux_2_17_x86_64、manylinux2014_x86_64平台标签表示在 ManyLinux 2014 或更新规范下的 x86_64 系统可运行。pip 在解析时会把自己当前环境的标签集合来自sysconfig.get_platform()、sys.implementation等与 wheel 文件名里的标签做交集。有交集就能装没交集就跳过。--platform、--python-version、--abi这三个参数的组合实际上就是帮你“伪造”目标环境的标签集合让 pip 认为当前就是在目标平台上操作。注意一点不是随便填个--platform就能成功。wheel 文件的平台标签必须和你指定的--platform恰好匹配或者能形成兼容关系。比如指定--platform manylinux2014_x86_64pip 会匹配所有平台标签为manylinux2014_x86_64或兼容的manylinux_2_17_x86_64包但不会匹配win_amd64。1.3 什么时候才需要手动指定平台我总结了几类必须手写平台参数的场景你可以对照一下自己属于哪种离线安装到同架构但不方便联网的环境比如本地 CentOS 7 下载目标也是 CentOS 7这种一般不用指定默认即可。但为了保险可以用--platform manylinux2014_x86_64 --python-version 3.x把包固定下来。跨 CPU 架构本地是 x86_64目标服务器是 ARM64aarch64或反之。这是最常见的需求。跨操作系统本地 Windows目标是 Linux或者本地 Linux目标是 macOS。特别是打包给同事、发布到容器镜像、构建交叉编译环境。目标环境的 Python 版本和本地不同比如本地 Python 3.12目标服务器还是 Python 3.8。即使操作系统一致也需要指定--python-version 38否则 cp312 的 wheel 在 cp38 下装不了。还有一类隐藏需求目标机器根本没有 Python 环境只是需要一个 wheel 包交给固件环境、嵌入式系统或者特殊运行时处理。这时候你就要精确指定--implementation和--abi甚至要查 Python 版本对应的 ABI 标签。2. 核心参数逐一拆解别瞎填2.1 --platform 怎么填才准确--platform接受的是 wheel 平台标签不是随便写个“linux”就行。它一般遵守manylinux、win、macosx、musllinux等规范。常见的写法目标环境平台标签示例Linux x86_64glibc 2.17manylinux2014_x86_64或manylinux_2_17_x86_64Linux ARM64glibc 2.17manylinux2014_aarch64或manylinux_2_17_aarch64Linux ARMv7 / ARMv8 32位manylinux2014_armv7lLinux x86_64musl libcAlpinemusllinux_1_2_x86_64Windows x64win_amd64Windows 32位win32macOS Intelmacosx_10_9_x86_64、macosx_10_10_x86_64等macOS Apple Siliconmacosx_11_0_arm64、macosx_10_9_universal2等这里有个细节容易踩坑很多现代 Linux 发行版的 wheel 只提供manylinux_2_28_x86_64而 older 的manylinux2014_x86_64可能没有对应版本。这时候你就要把--platform写得更具体比如manylinux_2_28_x86_64。但问题来了不是所有包都会为每个平台发布 wheel像一些纯 Python 库requests、urllib3实际平台标签是py3-none-any这类包任何平台都能用指定任意--platform都会拉下来。所以最稳妥的做法是在目标机器上执行pip debug --verbose看输出里Compatible tags列表从中挑一个和你要下载的平台一致的标签。比如 ARM64 服务器上会出现cp39-cp39-manylinux_2_17_aarch64 cp39-cp39-manylinux2014_aarch64 cp39-cp39-linux_aarch64那你下载时--platform manylinux2014_aarch64就是安全的它能匹配到 manylinux_2_17_aarch64 的包。后面我会给详细操作。2.2 --python-version 和 --implementation 的匹配规则--python-version填目标环境的 Python 版本但格式有讲究比如 Python 3.9 写39Python 3.11 写311不要带小数点。它对应 wheel 文件名里的 cp39/cp311 这样的标签。--implementation一般填cpCPython、ppPyPy、ipIronPython等。绝大多数场景填cp因为 PyTorch、numpy 等核心库通常只发 CPython 的 wheel。但如果你目标环境是 PyPy就要改成pp并且--abi也要跟着变化比如pypy39_pp73。需要注意--python-version不是孤立的它会和--abi一起决定最终的匹配范围。比如你指定--python-version 39 --implementation cp --abi cp39pip 只会下载cp39-cp39-*的 wheel。但如果目标 Python 3.9 编译时使用了较新的 ABI 策略可能既有cp39-cp39也有cp39-abi3stable ABI的 wheel。这时候如果你强制--abi cp39会把abi3的包全部过滤掉导致下载失败或包不完整。所以如果是下载目标环境的常规 CPython我一般建议--abi也写成同标签的 cpXY如果失败再放宽到--abi abi3或干脆不指定不指定时 pip 会默认用当前环境的 ABI 标签跨平台会有问题所以还是建议显式指定。--abi常见值对应关系Python 目标版本ABI 标签CPython 3.8cp38CPython 3.9cp39CPython 3.10cp310CPython 3.11cp311CPython 3.12cp312CPython 3.13cp313兼容稳定 ABIabi3纯 Python 无扩展none但有个更省事的小技巧当你不确定时--implementation cp --abi cp39就够用了。很多包同时满足多个 ABI 标签pip 会在候选集里挑选最合适的。2.3 --only-binary:all: 到底起了什么作用没有--only-binary:all:pip download 遇到找不到匹配 wheel 的包时会自动回头找 sdist 源码包。源码包可不是目标平台能用的下载下来还要在目标机器上现场编译既慢又容易缺编译工具链。更麻烦的是如果你指定了--platform是 ARM64但某些包没有 ARM64 的 wheelpip 可能直接拉个tar.gz下来你测了半天最后才发现这不是预编译产物。所以跨平台下载时我几乎总是写pip download --no-deps --only-binary:all: ...:all:的意思是“只用 wheel绝不接受源码包”。这么一来 pip 会明确报错而不是偷偷下载 sdist你就能及时知道这个包是不是不支持目标平台。如果确认目标平台没有 wheel那只能考虑目标机器上源码编译或者寻找其他替代包。补充一点--only-binary:all:和--no-binary:all:是相反的。前者只要二进制 wheel后者只要源码。跨平台下载默认是前者。2.4 --no-deps 和 -d 的使用时机很多人第一次用pip download会漏掉--no-deps。不写的话pip 会递归下载当前包的所有依赖这其实很多时候也是你想要的。但问题是依赖解析可能受到本地已安装包的影响也可能把一些当前平台独有的包比如colorama在 Windows 下才需要拉下来导致离线目录里出现冗余或平台不匹配的文件。我建议的做法分两步第一步先在目标机上用pip download -r requirements.txt -d /tmp/offline不指定--platform生成一份完整的依赖列表并下载因为这时候 pip 会按目标机的真实环境解析。第二步如果反过来是跨平台给目标机备包就在本地上台用--platform参数逐个包下载并且加上--no-deps避免把本地平台的依赖误拉进来。依赖关系自己单独处理。-d指定输出目录这个很简单但有个坑目录不存在时 pip 会自动创建所以不用提前 mkdir。可如果你用了--target安装到目录和-d搞混下载目录里会出现一堆.whl文件而不是被展开的包目录这是正常现象离线安装时直接用pip install --no-index --find-links/offline package即可。3. 实战给 Linux ARM64、Windows x64、macOS 分别下载包3.1 命令模板一本机 Windows给 Linux aarch64 下载 pandas假设你的开发机是 Windows目标服务器是 ARM64 架构、跑 Ubuntu 20.04、Python 3.9。要下载 pandas 及其依赖可以这样分步操作。第一步确定平台标签。ARM64 Ubuntu 20.04 的 glibc 是 2.31满足 manylinux_2_17 的要求所以平台标签写manylinux2014_aarch64或manylinux_2_17_aarch64都可以。Python 3.9 对应cp39ABI 也是cp39。第二步执行下载pip download \ --only-binary:all: \ --platform manylinux2014_aarch64 \ --python-version 39 \ --implementation cp \ --abi cp39 \ -d ./linux_arm64_pkgs \ pandas2.2.2这里的--no-deps先不加原因是 pandas 依赖 numpy、python-dateutil、pytz、tzdata 等如果不解析依赖离线目录不完整目标机上还得单独补。但用默认解析有一个风险本地 Windows 的依赖解析结果可能和 Linux 不完全一样。实际上 pip download 在指定--platform后依赖解析也是基于该平台标签进行的所以一般没问题。如果出现了依赖包缺失再把--no-deps打开逐个补下。下载完成后./linux_arm64_pkgs目录里应该出现类似pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl看到文件名里的aarch64就说明平台匹配正确。3.2 命令模板二本机 Linux给 Windows x64 下载 openpyxl换一种方向目标是 Windows 10 x64、Python 3.10。平台标签用win_amd64Python 标签cp310ABIcp310pip download openpyxl3.1.2 \ --only-binary:all: \ --platform win_amd64 \ --python-version 310 \ --implementation cp \ --abi cp310 \ -d ./win_amd64_pkgsopenpyxl 是纯 Python 包但依赖的 et_xmlfile 也是纯 Python所以这里其实不指定 platform 也能下载。但指定后能确保产物中不会因为某些平台特定依赖而出错。如果包里有 C 扩展比如lxml你就会看到lxml-*.whl的后缀是win_amd64这就对了。有一个容易忽略的点win_amd64标签的 wheel 文件在 Linux 本地可以对内容做校验比如用 unzip 查看但绝不能直接解开导入。你只需要确认文件名符合预期不要尝试在本地安装。3.3 命令模板三给 macOS Apple Silicon 下载 numpy目标 Mac 是 Apple SiliconM1/M2/M3、macOS 11.0、Python 3.11。平台标签可以写macosx_11_0_arm64也可以尝试macosx_10_9_universal2。universal2 的 wheel 同时支持 x86_64 和 arm64在 M 系列 Mac 上能装而且兼容性更好。pip download numpy1.26.4 \ --only-binary:all: \ --platform macosx_11_0_arm64 \ --python-version 311 \ --implementation cp \ --abi cp311 \ -d ./macos_arm64_pkgs如果你不确定目标 macOS 版本用macosx_10_9_universal2往往更保险因为 macOS 的部署目标往前往后都能兼容。但注意有些包只发布macosx_11_0_arm64有些只发 universal2由于 PyPI 上可能同时存在多个版本建议先到 PyPI 页面看该版本实际提供的 wheel 文件列表再决定。3.4 一次下载多个包requirements.txt 该怎么配合如果项目依赖比较多可以在目标平台上维护一份 requirements.txt然后在本地跨平台下载pip download \ --only-binary:all: \ --platform manylinux2014_x86_64 \ --python-version 311 \ --implementation cp \ --abi cp311 \ -r requirements.txt \ -d ./linux_x64_pkgs这比一条条执行快得多。但前提是 requirements.txt 里的版本要存在对应平台的 wheel否则 pip 会直接报错。遇到这种情况解决办法是看具体是哪个包判断它是不是纯 Pythonpy3-none-any如果是纯 Python其实可以不放在下载清单里直接让目标机从离线目录安装时读取依赖即可如果是有 C 扩展的包且没有目标平台 wheel那就只能换成支持目标平台的替代库或者在目标机上编译。3.5 怎么确认目标机的真实兼容标签不需要在目标机上装 Python 也可以查“通用标签列表”但更准确的方法是让有目标机的人执行一行命令pip debug --verbose输出里有一段Compatible tags: 372 cp312-cp312-manylinux_2_28_x86_64 cp312-cp312-manylinux_2_27_x86_64 ...把这里所有的manylinux_*标签拍下来选择范围更大的那个比如manylinux_2_28_x86_64说明系统 glibc 较新兼容manylinux2014。如果没有目标机环境也可以用python -m pip debug --verbose在本地模拟查看当前机器标签再根据 CPU 架构手写目标标签。经验法则glibc 2.17 对应 manylinux2014glibc 2.28 对应 manylinux_2_28glibc 2.35 对应 manylinux_2_35。在较新的发行版上能用manylinux_2_28就不必纠结manylinux2014。4. 常见报错与排查技巧4.1 “No matching distribution found” 是怎么回事这是跨平台下载中最常见的报错。出现这句话说明 pip 根据你给的--platform、--python-version、--abi组合在 PyPI 上没有找到满足条件的 wheel。常见原因有三个版本号对不上比如你指定了--platform manylinux2014_aarch64但该包最新版只提供 manylinux_2_28_aarch64或者干脆没有 aarch64 wheel。这时可以把版本放宽或者查 PyPI 文件列表确认。ABI 写得太死比如--abi cp39但包只发布了cp39-abi3的 wheel。遇到这种情况把--abi改成abi3或者不区分 ABI 直接一次尝试多个。Python 版本不存在对应 wheel比如你给 Python 3.7 下载某个包但该包最低支持 3.8那自然无解。排查时可以先用不带--platform的命令跑一次pip download 包名版本 --no-deps --only-binary:all: -d /tmp/test如果本地能下载说明 PyPI 有对应包问题出在平台的标签上。然后去 PyPI 的“Download files”页面手工看有哪些 wheel 后缀逆向得出该填什么参数。4.2 “is not a supported wheel on this platform” 出现在本机安装阶段有时候你辛辛苦苦下载好一包.whl拿到目标机上用pip install /path/to/lxml-*.whl安装结果报错不支持。这不一定是下载的参数错了可能是安装命令有问题。比如你把manylinux_2_17_aarch64的 wheel 拷到了 x86_64 的机器上自然装不了。或者你把win_amd64的 wheel 拷到了 32 位 Python 环境里。正确的做法是先在目标机上pip debug --verbose看看真实支持哪些标签再把它和 wheel 文件名的 platform tag 对齐。如果对不齐说明离线包给错了重新按目标平台下载。一个更隐蔽的坑同一个 wheel 文件名里可能包含多个平台标签比如numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl这个文件实际上既能匹配 manylinux_2_17_x86_64也能匹配 manylinux2014_x86_64。你在本地下载时用了前一个标签到了目标机上安装依然有效。所以文件名里多个标签不是错误反而说明它兼容范围更广。4.3 下载目录权限导致 “拒绝访问” 或 os error 5跨平台下载时如果你把-d指定到系统保护目录比如 Windows 的C:\Program Files\...或 Linux 的/root之外某个无写权限路径pip 会在写文件时报错可能看到类似error: 拒绝访问。(os error 5)的信息。这通常不是 pip 本身的问题而是目录权限不足。建议把下载目录放到用户目录下比如~/offline_pkgs或当前工作目录下的./offline。如果是 Windows避免放在C:\根目录或Program Files下。另外如果之前执行过 pip 时用了 sudo 或管理员终端后续普通终端可能因为缓存权限不一致而报错。这时候清理 pip 缓存即可pip cache purge这个报错和“os error 5”里的 os 没有任何关系它只是操作系统层面的错误码不用往系统或者平台架构上想先检查写路径。4.4 离线安装时提示 “has requirement xxx, but you have yyy”下载阶段不会报这个等到目标机离线安装时才出现原因是依赖版本冲突。常见于你下载时用了--no-deps导致依赖清单不完整或 requirements.txt 里的版本约束被安装到目标机上时目标机已有其他版本的库。我的建议是离线安装时依然使用一个完整的 wheelhousepip install --no-index --find-links./linux_arm64_pkgs -r requirements.txt这条命令会强制只用./linux_arm64_pkgs里的 wheel如果缺少依赖会明确提示缺哪个方便你从下载目录补。如果下载目录里已经包含了所有依赖但还是报冲突那就要检查 requirements.txt 的版本范围是否过窄比如同时要求 numpy1.24 和 pandas2.2pandas 2.2 需要 numpy1.22.4其实没问题但有些组合会有 bug这时候没有捷径只能调整版本约束让它们兼容。4.5 下载 py3-none-any 纯 Python 包时的特殊处理像requests、idna、certifi这样的包wheel 文件名通常是requests-2.31.0-py3-none-any.whl。它们的平台标签是anyABI 是none所以不管你指定什么--platform、--python-version只要 Python 支持 Py3都能下载。这类包也能直接放到任何平台的离线目录中。但注意如果你用了--abi cp39pip 在匹配py3-none-any时实际上会因为py3和cp39的兼容关系而成功。因为py3标签本身表示包装后可以运行于 Python 3 任何版本包含 cp39。所以不要因为文件名是py3-none-any就认为它是“废包”它在离线安装时是通用的。4.6 常用排查命令速查表场景命令查看当前环境兼容标签pip debug --verbose查看某个包的已发布文件直接去 PyPI 项目页面的 Download files 目录只下载不装忽略依赖pip download 包名 --no-deps -d ./pkgs强制只下载 wheelpip download 包名 --only-binary:all: -d ./pkgs查看下载目录里的包名和版本pip list --path ./pkgs离线安装目录中的所有包pip install --no-index --find-links./pkgs 包名清空 pip 缓存pip cache purge5. 跨平台下载的几条独家经验5.1 不要迷信 purelib 包也要注意包内部的动态依赖很多纯 Python 包虽然本身是py3-none-any但它会在安装时通过 setup.py 动态拉取系统库或者 ctypes 加载本机.so/.dll。这类包下载时没问题但离线安装到目标机后运行时才发现缺系统库。比如一些涉及 Bluetooth、GPU 调用、串口通信的库它们依赖系统级别的 libusb、libcuda 等。遇到这种情况光靠 pip 参数解决不了还得把对应系统依赖也放进离线部署方案里。5.2 用“target 目录”验证而不是强行安装有些人在本地下载完 Windows 的 wheel 后想临时验证能不能用会尝试pip install到本地 Python。这几乎必失败因为没有 Windows 平台的 Python 扩展能在 Linux 上导入。验证方法很简单只需要用unzip -l看看解压出来的.pyd或.so文件是否是目标平台对应的扩展名。比如 Windows wheel 里是xxx.pydLinux wheel 里是xxx.somacOS 里也是.so但依赖 Mach-O 格式。确认扩展名一致基本就能判断下载无误。5.3 一次下载多个 Python 版本的方案有时候目标环境不是单一版本比如同一台服务器上有 Python 3.8 和 Python 3.11 两个项目。可以分别下载两个目录pip download --platform manylinux2014_x86_64 --python-version 38 --implementation cp --abi cp38 -d ./wheelhouse/py38 -r requirements.txt pip download --platform manylinux2014_x86_64 --python-version 311 --implementation cp --abi cp311 -d ./wheelhouse/py311 -r requirements.txt这样目录是隔离的安装时也不用担心 cp38 的包被 3.11 引用。如果目标机器 CPU 架构不同比如同时有 x86_64 和 ARM64 节点目录加上架构后缀更好例如wheelhouse/arm64_py38、wheelhouse/x64_py311。5.4 优先使用目标机原生 Python 版本的关键字如果目标环境已经安装好了 Python最省事的是直接在目标机上执行python -m pip download -r requirements.txt -d ./offline不需要指定任何--platform、--python-version因为 pip 此时用的就是目标机的真实标签绝不会错。只有当目标机无法联网、或你需要在生产环境之外预先准备离线包时才需要手动指定参数。我经常遇到的情况是目标服务器连不上外网但有一台装了同样 Python 版本的跳板机那就在跳板机上下载连参数都不用写。5.5 版本锁定的重要性跨平台下载时如果 requirements.txt 里写的是不精确的版本比如numpy1.24pip 在不同时间下载到的版本会不同。今天下载 1.26.4明天可能变成 1.26.5这会让离线包清单不可复现。我的习惯是所有下载都用把版本钉死或者下载完后生成一份pip freeze结果下次直接按冻结版本下载。否则一旦目标环境已经安装过部分旧版本包依赖解析可能会挑一个和你离线目录不同的版本导致冲突。6. 写在最后的实操心得踩过几次坑之后我现在的跨平台下载流程基本固定了拿到目标机器的pip debug --verbose输出确认 Python 版本、glibc 和架构然后写一个带--only-binary:all:的下载命令把所有依赖装进一个 wheelhouse最后在目标机上用--no-index --find-links安装。这套流程走下来几乎没有再因为平台匹配问题翻过车。最后再分享一个小技巧如果你给 ARM64 Linux 下载不到某个包的 wheel不妨看看 PyPI 文件列表里是不是只有manylinux_2_28_aarch64而没有manylinux2014_aarch64那就把--platform改成manylinux_2_28_aarch64同时确认目标系统 glibc 版本大于等于 2.28。反正只要目标机 glibc 比 wheel 要求的新兼容性就是成立的不必死守 older 标签。