在 Python 3.15.0 上,抓取技术栈的大部分包都能从现成的 wheel 安装,并通过了冒烟测试。凡是依赖 brotli 或 zstandard 的包,在没有 C 编译器的机器上都会安装失败,因为这两个包都没有 3.15 的 wheel,连最普通的 pip install scrapy 也不例外。最快的解决办法是在 pip install 之前装上 gcc(见下文方案 1),或者在 brotli 发布 cp315 wheel 之前继续使用 Python 3.14(方案 2)。
最后检查时间:2026 年 10 月 16 日 10:09(UTC)。 当时 PyPI 上 brotli 1.2.0 和 zstandard 0.25.0 仍然没有 cp315 wheel。10 月 15 日发布的 zope.interface 8.7 已经提供 cp315 wheel。
| 包 | 版本 | cp315 wheel | 有编译器 | 无编译器 | 备注 |
|---|---|---|---|---|---|
scrapy | 2.19.0 | 纯 Python; 无(brotli 1.2.0) | 能装 | brotli 编译失败 | 自 2.18.0 起在 CPython 上依赖 brotli |
requests[socks] | 2.34.2 + PySocks 1.7.1 | 纯 Python | 能装 | 能装 | |
httpx[socks] | 0.28.1 + socksio 1.0.0 | 纯 Python | 能装 | 能装 | |
httpx[brotli] | 0.28.1 | 无(brotli 1.2.0) | 能装 | brotli 编译失败 | httpx brotlicffi 无需编译器即可安装 |
httpx[zstd] | 0.28.1 | 无(zstandard 0.25.0) | 能装 | zstandard 编译失败 | 编译最慢 |
aiohttp | 3.14.5 | 有 | 能装 | 能装 | |
aiohttp[speedups] | 3.14.5 + aiodns 4.0.4 | 无(Brotli 1.2.0) | 能装 | Brotli 编译失败 | aiohttp aiodns brotlicffi 可以安装 |
aiohttp-socks | 0.12.0 | 纯 Python | 能装 | 能装 | |
urllib3[brotli] | 2.8.0 | 无(brotli 1.2.0) | 能装 | brotli 编译失败 | urllib3 brotlicffi 可以安装 |
curl_cffi | 0.16.3 | abi3 | 能装 | 能装 | |
playwright | 1.64.0 | 不区分 Python 版本 | 能装 | 能装 | 未测试浏览器 |
lxml | 6.1.3 | 有 | 能装 | 能装 | |
beautifulsoup4 | 4.15.0 | 纯 Python | 能装 | 能装 | |
parsel | 1.12.1 | 纯 Python | 能装 | 能装 | |
selectolax | 1.0.0 | 有 | 能装 | 能装 | 1.0:selectolax.parser 抛出 ImportError,请改用 selectolax.lexbor |
pandas | 3.0.6 + numpy 2.5.4 | 有 | 能装 | 能装 | |
pydantic | 2.14.1 + pydantic-core 2.50.1 | pydantic-core: 有 | 能装 | 能装 | |
orjson | 3.13.0 | 有 | 能装 | 能装 | |
tenacity | 9.2.1 | 纯 Python | 能装 | 能装 |
“有编译器”和“无编译器”指官方 python:3.15-slim 镜像装与不装 gcc 两种情况。每个包都分别用 pip 和 uv 安装过,两者在所有用例中结果一致。纯 Python 包没有编译代码,所以不需要 cp315 wheel。每次成功的安装都通过了各自的冒烟测试。
在 Python 3.15 上 pip install scrapy 失败:Failed building wheel for brotli
在 slim 镜像里,pip 26.2.1 卡在 brotli:
× Building wheel for brotli (pyproject.toml) did not run successfully.
│ exit code: 1
error: [Errno 2] No such file or directory: 'gcc'
ERROR: Failed building wheel for brotli
error: failed-wheel-build-for-install
× Failed to build installable wheels for some pyproject.toml based projects
╰─> brotliuv 0.13.1 在同一个包上失败,并指出了依赖链:
error: Failed to build `brotli==1.2.0`
cause: The build backend returned an error
hint: `brotli` (v1.2.0) was included because `scrapy` (v2.19.0) depends on `brotli`这条依赖链很短。Scrapy 2.19.0 是纯 Python 包,但它要求 brotli>=1.2.0; implementation_name != "pypy",所以在 CPython 上的每次安装都会带上 brotli。这个依赖是 Scrapy 2.18.0(2026 年 8 月 20 日)加入的,2.17.0 还没有。brotli 1.2.0 发布于 2025 年 11 月 5 日,提供 CPython 2.7 和 3.6 到 3.14 的 wheel,没有更新的版本。找不到匹配的 wheel 时,pip 和 uv 会下载源码包并编译 _brotli 扩展,而这需要 gcc。httpx[brotli]、aiohttp[speedups] 和 urllib3[brotli] 在 CPython 上同样需要 brotli 包,httpx[zstd] 需要 zstandard 0.25.0,所以它们也会遇到同样的问题。
办法一:在镜像里装上编译器
在 slim 镜像中运行下面这一行,所有失败的包就都能装上了:
apt-get update && apt-get install -y --no-install-recommends gcc libc6-dev它装上了 gcc 14.2.0。之后 pip install scrapy 用 28 秒完成而不再失败,uv 用了 19 秒,爬虫也抓取了测试页面,其中包括一个 brotli 压缩的页面。zstandard 编译最慢:用 pip 安装 httpx[zstd] 花了 63 秒。在 Dockerfile 里,把这一行放在 pip install 之前。
只测试了这一行 Debian 命令。在 Windows 上,这类编译所需的编译器包含在微软的 C++ Build Tools 中;我们没有测试 Windows。
办法二:在 wheel 发布前继续使用 Python 3.14
Python 3.14 有 brotli 和 zstandard 的 wheel,所以不存在这个问题。想知道 brotli 是否已经跟上,用 Python 3.15 运行:
python3 -m pip download --only-binary :all: --no-deps -d /tmp/wheel-check brotli10 月 16 日运行时,它仍然以 ERROR: No matching distribution found for brotli 结束。一旦它下载到 .whl 文件,就说明 cp315 wheel 已经发布,pip install scrapy 不再需要编译器。
上游方面,google/brotli-wheels 的 issue #25“Python 3.15 wheels missing”于 10 月 9 日创建,截至 10 月 16 日没有任何回复。google/brotli 的 pull request #1583 增加了一个构建 cp315 wheel 的 CI 任务。截至 10 月 16 日,它仍处于打开状态、没有审查,唯一的评论来自 CLA 机器人。它的说明中写道,仍需要维护者把 wheel 发布到 PyPI。
不用 Scrapy?brotlicffi 有 wheel
brotlicffi 1.2.0.2 提供 abi3 wheel,pip 在 3.15 上安装成功。HTTPX、urllib3 和 aiohttp 都接受用它代替 brotli,所以在没有编译器的情况下,下面几种组合都能安装,并解压了 brotli 压缩的响应:
pip install httpx brotlicffi
pip install urllib3 brotlicffi
pip install aiohttp aiodns brotlicffi最后一行相当于去掉 Brotli 依赖的 aiohttp[speedups]。Scrapy 走不通这条路:在 CPython 上它要求的就是 brotli 包本身。
zstd 比看上去简单。在 Python 3.14 及更新版本上,urllib3 2.8.0 和 aiohttp 3.14.5 使用标准库的 compression.zstd 解压 zstd,所以两者都向服务器声明了 zstd,并在不装任何额外包的情况下解压了 zstd 响应。只有 httpx[zstd] 需要 zstandard。
不要盲目使用 --only-binary
--only-binary :all: 让 pip 永远不从源码构建。它看起来是最直接的变通办法,而且不会报错:
pip install --only-binary :all: "httpx[brotli]" "aiohttp[speedups]"pip 在回溯时输出了 INFO: This is taking longer than usual,最后装上的是 httpx 0.18.2(2021 年 6 月)和 aiohttp 0.13.1(2014 年 12 月)。唯一的其他提示只有一行:
WARNING: aiohttp 0.13.1 does not provide the extra 'speedups'解析器一路回退到旧版本,直到找到所有依赖都能只用 wheel 满足的版本。uv pip install --no-build 选中了同样的两个版本,并输出 warning: The package `aiohttp==0.13.1` does not have an extra named `speedups` 。
只对一个包使用这个参数也不安全。pip install --only-binary brotli scrapy 同样以退出码 0 结束,装上的是 Scrapy 2.17.0,也就是最后一个不依赖 brotli 的版本。uv 的结果相同。这个 Scrapy 抓取了测试页面,但它只向服务器声明 gzip, deflate,而带 brotli 的 2.19.0 声明的是 gzip, deflate, br, zstd。如果你的爬虫使用 async def start(),2.17.0 仍然可以运行它们。
要发现这种悄无声息的降级,每次安装后都看一下实际装上的版本:
pip list
uv pip list要让它明确报错,就在参数旁边锁定你想要的版本:
pip install --only-binary brotli "scrapy==2.19.0"ERROR: Could not find a version that satisfies the requirement brotli>=1.2.0; implementation_name != "pypy" (from scrapy) (from versions: none)
ERROR: No matching distribution found for brotli>=1.2.0; implementation_name != "pypy"uv 在同样锁定版本时停在 error: No solution found when resolving dependencies。在 requirements.txt 里为顶层包写明确切版本,每次构建都能得到同样的保护。
10 月 15 日已修复:没有 C 加速的 zope.interface
Twisted 以及依赖它的 Scrapy 都依赖 zope.interface。zope.interface 8.6 的 wheel 只到 CPython 3.14。10 月 14 日,没有编译器时,pip 和 uv 仍然在 3.15 上装上了它:C 扩展无法编译时,它的构建会退回到纯 Python。安装输出里既没有错误,也没有警告。
代价体现在基本的接口检查上。在实验中,IThing.providedBy(obj) 没有 C 扩展时每次调用耗时 271 纳秒,有 C 扩展时为 101 纳秒。实验没有测量这对一次完整抓取意味着什么。
10 月 15 日发布的 zope.interface 8.7 提供了 cp315 wheel。10 月 16 日,在没有编译器的环境中全新安装时,pip 和 uv 都拿到了 8.7 的二进制 wheel,C 扩展可以导入,同样的检查耗时 102 纳秒。10 月 15 日之前创建的虚拟环境仍是纯 Python 的 8.6,直到你升级 zope.interface。
查看你用的是哪一种:
python -c "import zope.interface._zope_interface_coptimizations"出现 ModuleNotFoundError 就说明用的是纯 Python 后备实现。使用 8.7 的 wheel 或装上 gcc 后,导入都成功了。
3.15 中对抓取有影响的其他变化
大多数抓取程序最先注意到的是 UTF-8 模式,它现在默认开启(PEP 686)。What's New in Python 3.15 写道,没有显式指定编码的 I/O 操作,例如 open('flying-circus.txt'),现在无论系统环境如何都使用 UTF-8。在实验中,设置 LANG=C 时,3.15 上不带 encoding 的 open() 写出的是 UTF-8;加上 -X utf8=0 后则退回 ANSI_X3.4-1968,也就是 ASCII。
对抓取来说,这主要体现在导出上。用 open(path, "w") 写出的 CSV 或 JSON 文件现在在任何机器上都是 UTF-8,包括 3.14 会使用区域设置代码页的 Windows 系统。这通常正是你想要的。如果下游工具需要旧编码,传入 encoding="locale" 或设置 PYTHONUTF8=0;更好的做法是在写入和读取时都显式指定编码。
如果你同时升级 Scrapy
升级到 Python 3.15 时往往也会换上新版 Scrapy。从 2.16 起,Scrapy 不再调用 start_requests():只定义了这个方法的爬虫会打开、什么都不发送,然后以完成状态关闭,没有任何警告。升级后 Scrapy 抓取 0 个页面给出了一行就能解决的办法 async def start(),本实验中的爬虫用的正是它。
方法与局限
记录于 2026 年 10 月 14 日,在一台 x86_64 Ubuntu 主机上,使用官方 python:3.15-slim 镜像的文件系统(Debian 13 trixie,索引摘要 sha256:ea4f1f29…,构建于 2026 年 10 月 12 日),在独立的 Linux 命名空间而不是 Docker 中运行。解释器报告为 Python 3.15.0 (main, Oct 12 2026, 22:33:31) [GCC 14.2.0],自带 pip 26.2.1,另装 uv 0.13.1。有编译器的副本用上面的 apt 命令装上了 gcc 14.2.0。表格来自这些运行。10 月 16 日,我们在没有编译器的环境中重新运行了 Scrapy、--only-binary brotli 和 zope.interface 这几项:除了 zope.interface 现在来自 8.7 的 wheel 之外,结果都与 10 月 14 日一致。
每个用例都使用新的虚拟环境,并且不使用包缓存。安装的版本,以及每个包来自二进制 wheel、纯 Python wheel 还是本地构建,都是从 wheel 标签中读取的。冒烟测试访问的是本地 http.server,它只使用客户端声明支持的编码进行压缩。Playwright 已安装,但没有安装浏览器。全程没有使用代理。
本实验不涉及 macOS、Windows、ARM、基于 musl 的镜像或自由线程构建。PyPI 每天都在变化,所以在根据表中任何一行采取行动前,请重新运行 wheel 检查。脚本、冒烟测试和原始输出都在下载目录中:lab315.py、results.csv、results.json,以及 10 月 16 日的重新运行结果 results-2026-10-16.json。
相关内容
- Scrapy 2.19 代理设置:认证、轮换,以及 Scrapy 记录的代理错误。
- aiohttp 代理和 curl_cffi 代理:两者在 3.15 上都能从 wheel 安装。
- 修复 "Missing dependencies for SOCKS support":这个技术栈中的另一个安装错误。
- 我需要多少 GB 代理流量?:技术栈跑起来之后估算流量。压缩在那里很重要:声明的编码越少,客户端下载的字节可能越多。
ipvolt 正在为开发者和智能体构建代理基础设施。加入候补名单,开放访问时只发一封邮件。
参考来源
- Python.org:Python 3.15.0 发布页面
- What's New in Python 3.15:UTF-8 成为默认编码
- PEP 686: Make UTF-8 mode default
- PyPI:brotli 1.2.0 文件列表
- PyPI:zstandard 0.25.0 文件列表
- PyPI:zope.interface 8.6 文件列表
- PyPI:zope.interface 8.7 文件列表
- PyPI:Scrapy 2.19.0
- google/brotli-wheels issue #25:Python 3.15 wheels missing
- google/brotli pull request #1583:Build and test CPython 3.15 wheels in CI
- Microsoft C++ Build Tools