在 macOS 上用 venv 干净地安装 Camoufox:绕开 PEP 668 的正确姿势
- PEP 668 强制要求使用虚拟环境安装 Python 包,避免污染系统环境
- 创建虚拟环境命令为 python3 -m venv .venv,然后调用 .venv/bin/pip 安装依赖
- 安装 Camoufox 后需运行 .venv/bin/camoufox fetch 下载浏览器二进制和 GeoIP 数据库
- 使用 Camoufox 时注意 goto 后需等待页面加载,异步 API 必须在协程函数内使用
最近想试一下 Camoufox——一个基于 Firefox 的反检测浏览器,专为爬虫和 AI Agent 设计,每次启动都会生成一份新的浏览器指纹。听起来很美好,但在 macOS 上第一次安装就撞了一堵墙:pip install 直接被拒绝。
这篇文章记录完整的安装过程,并把每一步命令拆开讲清楚。Camoufox 只是引子,真正想讲的是 macOS 上 Python 包管理的标准动作。
一、问题:externally-managed-environment
按官方文档的推荐姿势,安装命令应该是这样:
pip install -U camoufox[geoip]
但在最新版 macOS(或任何使用 Homebrew Python 的系统)上,你会直接拿到这样一个报错:
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
xyz, where xyz is the package you are trying to install.
...
这不是 Camoufox 的锅,而是 PEP 668 在拦你。从 2023 年开始,所有由系统包管理器(Homebrew、apt 等)安装的 Python 都会带一个 EXTERNALLY-MANAGED 标记,告诉 pip:「这个 Python 是操作系统管的,你别往里乱装东西,免得污染系统环境导致系统工具坏掉。」
报错信息本身会给你三条出路:
brew install xyz——只对 Homebrew 已经收录的包有效,第三方库基本没戏--break-system-packages——硬上,不推荐,等于自废武功- 使用虚拟环境(venv 或 pipx)——这就是正解
二、解决方案:python3 -m venv
虚拟环境的思路很简单:在每个项目目录下复制一份独立的 Python 沙盒,所有依赖都装在这个沙盒里,系统 Python 保持纯净。Python 3.3 以后,venv 已经是标准库的一部分,开箱即用,不需要额外装任何东西。
完整命令
python3 -m venv .venv && .venv/bin/pip install -U pip 'camoufox[geoip]'
这条命令用 && 串了两段,含义逐段拆解如下。
第一段:python3 -m venv .venv
python3 -m venv—— 通过-m加载 Python 标准库的venv模块来创建虚拟环境(而不是调用某个独立的venv命令,这是 Python 工具链的通用模式).venv—— 虚拟环境的目录名。.venv是社区约定俗成的名字,前面带点表示隐藏目录,VS Code、PyCharm 等编辑器会自动识别并激活
执行完后,当前目录会出现一个 .venv/ 文件夹,结构大致如下:
.venv/
├── bin/ # python、pip、activate 等可执行文件
├── lib/ # 独立的 site-packages,所有依赖都装在这里
└── pyvenv.cfg # 标识这是一个虚拟环境
第二段:.venv/bin/pip install -U pip 'camoufox[geoip]'
.venv/bin/pip—— 直接调用虚拟环境里的 pip,不依赖source activate,更稳妥,也方便在脚本和 CI 里使用install—— 安装包-U——--upgrade的简写,已装则升级到最新版pip—— 先升级 pip 自己,避免后续装包时遇到旧版 pip 的兼容性问题'camoufox[geoip]'—— 装camoufox包,并启用名为geoip的 extra(可选附加依赖)
这里的方括号语法对应 pyproject.toml 里的 [project.optional-dependencies]。对 Camoufox 来说,geoip extra 会额外装上 geoip2,配合代理使用时可以根据 IP 自动匹配地理位置指纹,强烈建议带上。
为什么要加单引号? 因为 zsh 会把 [geoip] 当作通配符去匹配文件名,导致 no matches found。用单引号包起来,shell 就会原样传给 pip。Bash 用户不加引号也没事,但加了更保险。
为什么用 && 而不是 ;
&& 表示前一条成功才执行后一条。如果 venv 创建失败(比如目录已存在或权限问题),就不会去执行 pip,避免污染系统环境。; 则是不管成败都执行。除非你明确想忽略错误,否则一律用 &&。
三、下载浏览器二进制:camoufox fetch
pip 装好的只是 Python 绑定,Camoufox 真正的浏览器二进制(一个魔改版 Firefox)需要单独下载:
.venv/bin/camoufox fetch
这一步是首次运行的硬性要求,缺它 Camoufox 会因为找不到浏览器而启动失败。下载内容包括两部分:
- 浏览器本体——大概几十 MB 的压缩包,解压后放在
~/.cache/camoufox(macOS 默认路径) - GeoIP 数据库——66 MB 左右的 MaxMind 数据,让
geoipextra 真正能用
下载过程有进度条,耐心等就行。以后 Camoufox 版本更新时,这条命令也会负责增量更新。
四、运行:venv 内 vs venv 外
接下来就可以跑了。两种姿势:
方式 A:直接用虚拟环境的 Python
.venv/bin/python test1.py
最直接,不依赖 shell 状态,适合脚本和 CI。
方式 B:激活虚拟环境
source .venv/bin/activate
python test1.py
deactivate # 用完退出
激活后 python 和 pip 都会指向虚拟环境内的版本,命令行提示符前会显示 (.venv)。适合长时间开发。
五、Camoufox 的几个常见坑
环境装好后,写代码时还有几个坑容易踩。这里一并记下。
坑 1:goto 之后没有等待,浏览器瞬间关闭
下面这段代码看起来没毛病:
from camoufox.sync_api import Camoufox
with Camoufox() as browser:
page = browser.new_page()
page.goto("https://mp.weixin.qq.com/s/xxxxx")
但 with 块一退出,浏览器立即被关闭。你什么都看不到,也会怀疑是不是装错了。解决方式是加一行等待:
page.wait_for_load_state("networkidle") # 等网络空闲
# 或调试时直接显示窗口
with Camoufox(head=False) as browser:
...
坑 2:async with 写在顶层会 SyntaxError
Camoufox 提供了同步和异步两套 API。异步版本写起来很自然,但下面这样会直接报语法错误:
from camoufox.async_api import AsyncCamoufox
async with AsyncCamoufox() as browser: # SyntaxError
page = await browser.new_page()
await page.goto("https://example.com")
原因是 async with 必须在协程函数内部使用,不能写在模块顶层。正确写法是包一层 async def,然后用 asyncio.run 驱动:
import asyncio
from camoufox.async_api import AsyncCamoufox
async def main() -> None:
async with AsyncCamoufox() as browser:
page = await browser.new_page()
await page.goto("https://mp.weixin.qq.com/s/xxxxx")
await page.wait_for_load_state("networkidle")
print("title:", await page.title())
if __name__ == "__main__":
asyncio.run(main())
坑 3:Python 版本太新
Camoufox 依赖的 Playwright、numpy、lxml 都需要预编译的 wheel。如果你用的是刚发布不久的 Python 版本(比如 Python 3.14 刚出那阵子),可能某些依赖还没有对应的 wheel,pip 会回退到本地编译,缺工具链时直接失败。出问题前先确认一下 Python 版本是否在 Camoufox 官方支持的范围内。
六、一些延伸:venv vs pipx vs uv
虚拟环境这条路不止 venv 一种选择,按场景挑工具:
| 工具 | 适用场景 | 特点 |
|---|---|---|
python3 -m venv | 项目级依赖 | 标准库自带,零额外依赖 |
pipx | 安装独立的 CLI 工具 | 每个工具一个隔离环境,全局可调用 |
uv | 新兴的全功能工具 | Rust 写的,速度比 pip 快 10–100 倍,集 venv + pip + pipx 于一体 |
如果你只是想用 Camoufox 写爬虫脚本,venv 完全够用。如果你已经在用 uv,等价的命令更短:
uv venv
uv pip install 'camoufox[geoip]'
结语
PEP 668 不是 Python 在刁难人,而是社区在补 20 年来「pip 全局装包搞坏系统」的老账。venv 也就三五行命令的事,但第一次撞墙确实容易卡住。把流程走通一遍之后,这套隔离思路会一直受用——不止 Camoufox,未来装任何 Python 项目都是同一套动作。
Camoufox 本身的故事也值得一读。它是一个 Firefox fork,专门针对指纹检测做了大量改造(Canvas、WebGL、字体、屏幕分辨率等等),配合 Playwright 的 API 用起来非常顺手。下次再单独写一篇讲它的实战用法。
评论互动