跳到正文

Chrome 扩展

启动时通过 extensions 加载未打包的扩展。Manifest V2 和 V3 都能运行,有头和无头模式均可,支持 Windows 和 Linux。

加载扩展

把一个未打包扩展目录列表(每个目录都是包含 manifest.json 的文件夹)传给 launch()、launch_persistent_context() 或 serve()(.NET:Extensions = new[] { ... })。扩展会加载到浏览器的 profile 中,因此只有无痕模式的启动方式无法加载它们(ephemeral_profile=False、异步 API 的 launch()、.NET 的 LaunchAsync)。扩展仅适用于你通过 SDK 启动的浏览器;托管浏览器不会加载扩展。

python
from clearcote import launch_persistent_context

ctx = launch_persistent_context(
    "./profile",
    extensions=["./ext/ublock", "./ext/my-helper"],   # unpacked directories
)
page = ctx.pages[0] if ctx.pages else ctx.new_page()
page.goto("https://example.com")
javascript
import { launchPersistentContext } from "clearcote";

const ctx = await launchPersistentContext("./profile", {
  extensions: ["./ext/ublock", "./ext/my-helper"],
});

SDK 会同时替你设置 --load-extension 和 --disable-extensions-except。这两者必须搭配使用:在自动化模式下,单独使用 --load-extension 会被忽略,这通常就是扩展悄无声息地没有出现的原因。

获取未打包的目录

Clearcote 加载的是文件夹,而不是 .crx 文件。.crx 就是一个带签名头的 ZIP 包,解压后即可得到可加载的目录:

bash
unzip extension.crx -d ./ext/extension

Clearcote 无法从 Chrome Web Store 安装或更新扩展——上游的隐私补丁移除了这项集成。请把未打包的目录随你的自动化程序一起分发,并自行更新:以这种方式加载的扩展不会自动更新。Playwright 和 Puppeteer 采用的也是同样的模式。

Manifest V2 依然可用

原版 Chrome 已不再运行 Manifest V2 扩展。Clearcote 仍然可以,因为上游的隐私补丁集恢复了这项支持——因此,仅支持 MV2 的内容拦截器和较老的内部工具,在 Chrome 中失效之后,在这里仍能继续使用。

python
# both of these load and run
ctx = launch_persistent_context("./profile", extensions=["./ext/mv2-tool", "./ext/mv3-tool"])

无头模式

扩展在无头模式和有头模式下都能加载。无需额外参数,也不必为了让扩展工作而专门运行一个显示服务器。

python
ctx = launch_persistent_context("./profile", extensions=["./ext/my-helper"], headless=True)

指纹方面的考量

扩展是运行在你的 profile 中的你的代码,Clearcote 不会隐藏它。由此会带来两点影响,都值得你事先想清楚,而不是事后才发现。

  • 扩展是可被观察到的。改写 DOM、注入样式或拦截请求的内容脚本,会改变页面测量到的结果。站点只要把当前的标记与自己下发的标记进行比较,就能判断出页面被改动过。所有装了扩展的浏览器都是如此,这并不是 Clearcote 特有的信号,但它确实是一个信号。
  • Web 可访问资源可被探测。如果扩展声明了 web_accessible_resources,任何页面都可以尝试获取 chrome-extension://<id>/<file>,从而得知该扩展存在。优先选择不声明此类资源的扩展;对于必须声明的扩展,请考虑使用 use_dynamic_url。

如果你的目的是拦截请求而不是提供 UI,可以考虑在自动化层用 CDP 的 Network.setBlockedURLs 来实现——它按 URL 模式拦截,不会增加扩展暴露面,而且会保留浏览器缓存(Playwright 的 page.route() 也可行,但会关闭缓存)。通用原则见推荐设置:只伪装你需要的部分,只增加确有必要的暴露面。

故障排查

  • 没有任何反应。检查路径是否指向包含 manifest.json 的那个文件夹,而不是它的上级目录,也不是某个 .crx 文件。
  • 内容脚本始终不运行。确认 matches 模式覆盖了你正在访问的 URL——http://localhost/* 不匹配 http://127.0.0.1:8080/。
  • 有头模式正常,无头模式不行。在这个构建上不应出现这种情况;两种模式都能正常工作。如果你遇到了,请附上 manifest 提交 issue。