Obscura 无头浏览器实测:30MB 内存跑爬虫,Playwright 零改动切换

Headless Chrome 单实例 200MB、启动 2 秒,跑爬虫成本太高。Obscura 用 Rust 从零写了个引擎,30MB 内存、85ms 加载、内置反检测,还能让 Playwright 和 Puppeteer 零改动接过来。附实测数据、安装方式与四个踩坑记录。
Obscura Rust 无头浏览器实测与部署教程

📥 下载地址:Obscura(Apache-2.0 开源,34.6k stars)

GitHub Releases:github.com/h4ckf0r0day/obscura/releases/latest

官网:obscura.sh

最新版本 v0.2.6,Windows / Linux / macOS 均有预编译包,Windows 包 65.9 MB(stealth 版 69.7 MB)

跑过爬虫的人大概都遇到过同一个场景:脚本要抓几十个页面,一开并发内存就飙到几个 G,风扇狂转,跑到一半 OOM,只能把并发降到个位数慢慢熬。

问题不在你的代码,在于你用的工具。Headless Chrome 本质上是把一个完整的桌面浏览器塞进无头模式——它要做的事你根本用不上,但它们的开销你一分不少都得付。

Obscura 这个项目就是冲着这件事来的。它用 Rust 从零写了一个无头浏览器引擎,砍掉所有跟”给人看”相关的部分,只保留”给机器用”的能力。结果是:单实例内存约 30 MB,二进制 70 MB 左右,页面加载 85 毫秒。而它对外提供的接口是 Chrome DevTools Protocol——你现有的 Playwright 和 Puppeteer 代码,一行都不用改。

一、先算清楚:Headless Chrome 到底浪费在哪

这不是”Chrome 做得不好”,是设计目标不一样。Chrome 是给人用的浏览器,无头模式只是把窗口藏起来,底下那一整套东西还在。

1.1 它能做但你不需要的事

一个完整的 Chromium 包含:Blink 渲染引擎(排版、绘制、合成)、GPU 进程、多进程沙箱、扩展系统、媒体解码管线、字体渲染、无障碍树、拼写检查、自动更新……你的爬虫只需要三件事:

  • 把 HTML 拉回来并解析成 DOM;
  • 执行页面上的 JavaScript;
  • 让我读取节点、执行点击、拿到网络响应。

剩下那九十多项,全是白白付账。

1.2 开销的具体形态

更麻烦的是 Chrome 的多进程架构。每开一个实例就是一组进程(浏览器主进程、渲染进程、GPU 进程、工具进程),进程间通信要序列化、要共享内存、要同步。单个实例时感觉不出来,一旦并发到 20、50,调度开销会指数级放大。

这就是为什么很多人写爬虫时会遇到”加并发不提速反而更慢”的怪现象——CPU 全耗在进程切换和 IPC 上了,真正用来解析页面的时间占比不到三成。

开销项 Headless Chrome 对爬虫的实际意义
每实例内存200 MB 起16 GB 机器最多跑几十个
冷启动约 2 秒短任务里启动时间占一半
磁盘占用300 MB 以上容器镜像臃肿
进程模型多进程 + IPC并发越高调度成本越高
反检测无,`navigator.webdriver = true`基础风控一眼识别

二、Obscura 是什么:重写,不是套壳

先把这个区别说清楚,因为它决定了后面所有数据是不是可信。

2.1 它不是 Chromium 的裁剪版

市面上大部分”轻量浏览器”是在 Chromium 基础上改编译参数、关功能模块。这种做法的天花板很明显:Blink 渲染引擎的骨架还在,你就没法真正瘦下来。

Obscura 是另写一个引擎。它用了 V8(Chrome 的 JavaScript 引擎,这个确实是自己写不出来),但渲染、网络、DOM、协议层全部重新实现。项目的 6 个 crate 分工是这样的:

Crate 职责 实现
obscura-domHTML 解析与 DOM 树html5ever
obscura-netHTTP 客户端、Cookie、tracker 拦截自研 + 3520 域名黑名单
obscura-jsJS 运行时与 DOM 绑定deno_core(V8)
obscura-browser页面编排、导航、生命周期自研
obscura-cdpChrome DevTools Protocol 服务自研
obscura-cli命令行:serve / fetch / scrape自研

2.2 项目本身的量级

截至写这篇的时候(2026 年 11 月)GitHub 上的数据:

  • 仓库:h4ckf0r0day/obscura,Apache-2.0 协议;
  • 34,648 stars、2,697 forks;
  • 首个提交 2026 年 4 月,最近一次推送就在今天;
  • 最新版本 v0.2.6(2026-10-31),这个版本 117 个提交,主要改请求拦截、frame 导航、CDP 响应速度和 JS 启动;
  • 官网 obscura.sh。

半年做到三万多星、连续上 GitHub Trending 第一,这个曲线在基础设施类项目里很少见——大部分同类项目都是慢慢熬出来的。原因大概是它踩中了一个真实痛点:跑 AI Agent 的人突然发现浏览器太贵了。

2.3 别和 WebView2 / Electron 搞混

经常有人问”它跟 Electron 有啥区别”,这里理一下,因为这三者的定位完全不同。

Electron 是”用网页技术写桌面应用”——它内嵌 Chromium,给你一个完整的浏览器环境去渲染界面。它的目标是”让开发者不用学 C++ 也能做桌面软件”,代价是每个应用几百兆、内存几百兆。

WebView2 是”调用系统里的浏览器内核”——Windows 上直接用 Edge 的引擎,体积小一些,但本质上还是完整渲染引擎,而且依赖系统装了运行时。

Obscura 是”给程序用的、没有界面的取数工具”——它压根不打算渲染给人看,只提供 DOM、文本、元素操作和网络访问。所以它能做到 30 MB,而前两个做不到。

换句话说:Electron 和 WebView2 解决的是”怎么把人机界面做出来”,Obscura 解决的是”怎么用最低成本把网页数据拿出来”。混用场景很少,选型时别放在一起比。

三、30 MB 是怎么省出来的

理解了这个,你才知道它的能力边界在哪。

3.1 留下的:V8

JavaScript 执行必须靠真引擎。自己写一个 JS 解释器不是不行,但兼容性会被现实按在地上摩擦——一个现代页面的 JS 依赖多少边界行为,只有跑过才知道。所以 Obscura 直接嵌 V8,通过 deno_core 把这个引擎包起来。

这一步保住了核心能力:SPA 能跑、动态渲染能出、fetch 和 XHR 能执行、React/Vue 应用能正常初始化。

3.2 砍掉的:Blink 的渲染管线

人看网页需要什么?布局计算(Layout)、绘制(Paint)、图层合成(Composite)、GPU 上传、字体光栅化、动画帧调度。这一整套是 Blink 的主要工作量,也是内存占用的大头。

但机器不需要”看见”。你要的是 DOM 树、文本内容、元素坐标、属性值——这些在布局阶段就已经算出来了,后面的绘制和合成纯属浪费。

Obscura 保留了布局计算(因为要根据坐标点击元素),砍掉了绘制与合成。这一刀砍下去,内存和 CPU 都下来了。

3.3 顺带砍掉的

  • 扩展系统(爬虫不用插件);
  • 媒体解码管线(不播放视频);
  • 书签、历史、同步、密码管理;
  • 无障碍树(大部分场景用不上,需要时可单独开);
  • 多进程沙箱(改成单进程内的隔离,省掉 IPC)。

单进程这一点值得单独说:它既是内存省下来的关键,也是它稳定性不如 Chrome 的根源——一个页面把进程搞崩,同进程的其他页面一起完蛋。后面讲踩坑会提到。

四、硬碰硬:实测数据对比

官方 README 给的数据是这样,我把它们整理在一起,同时标注哪些是我实际验证过的。

指标 Obscura Headless Chrome 倍数
单实例内存约 30 MB200 MB+1/7
二进制体积约 70 MB300 MB+1/4
启动时间< 50 ms约 2 s1/40
静态页加载约 51 ms约 500 ms1/10
动态页加载约 84 ms约 800 ms1/9
反检测内置 stealth无—

4.1 并发下的差距才是真差距

单实例的对比其实不够说明问题,因为 200 MB 和 30 MB 在单页抓取时都无所谓。真正的分水岭在并发。

有一份第三方实测(8 个并发进程):

  • Obscura:约 21 页/秒,峰值内存 132 MB;
  • Headless Chrome:约 2.8 页/秒,峰值内存 7.1 GB。

换算一下:一台 32 GB 内存的机器,跑 Obscura 可以上千实例,跑 Chrome 大概一百六十个到顶。这是接近一个数量级的差别,不是调参能追上的。

4.2 但别把”85 毫秒”当基准

这里要说句实话。官方那个 85 ms 测的是简单静态页;换成真实站点,数字会差很多。

有一份公开的实测记录是拿 Cloudflare 防护的站点做对比(同一个页面、预热状态):

  • Obscura:约 4.1~4.9 秒,能拿到真实 HTML;
  • 真实 Chrome over CDP:约 5.1 秒(预热)/ 9.3 秒(冷启动);
  • 纯 curl:0.5~0.9 秒,但只拿到 Cloudflare 的挑战页,没有内容。

结论很清楚:在真实复杂站点上,Obscura 的速度优势没有宣传的那么夸张,但仍然比 Chrome 快一倍左右(冷启动时快更多),而且因为它不带 Chrome 的进程开销,并发扩展性好得多。

别被”85 毫秒”误导成”所有站点都快 10 倍”。它省的是固定开销(启动、内存、进程调度),省不掉网络往返和服务端响应时间。

五、安装:三条路,选一条

5.1 直接下预编译包(推荐)

Release 页面提供 20 个资产,覆盖 Linux / macOS / Windows,每个平台又分四种组合:

包名后缀 Windows 体积 含义
(无后缀)65.9 MB完整版,含渲染
-stealth69.7 MB完整版 + 反检测(TLS 指纹层)
-no-render39.2 MB去掉渲染,只拿 DOM 和文本
-no-render-stealth43.0 MB最省,纯抓取 + 反检测

🔧 我整理的软件库

这些年折腾服务器、虚拟化和开发环境,装过的系统镜像、运维工具、开发套件、数据库与中间件,我都归档在自建的文件库里,持续补充新版本。没有弹窗广告,不强制注册,需要什么直接取。

进入软件库 →

常用工具随手可取 · 不定期补档

Windows 用户直接下 obscura-x86_64-windows.zip,解压得到一个 exe,放进 PATH 就能用。不需要装 Node、不需要装 Chrome——这是它跟 Puppeteer 系工具最大的体验差别。

5.2 从源码编译

git clone https://github.com/h4ckf0r0day/obscura.git
cd obscura

# 基础版(无 stealth,不需要额外依赖)
cargo build -p obscura-cli --bin obscura --release

# stealth 版(需要 cmake,会拉 wreq / BoringSSL)
cargo build -p obscura-cli --bin obscura --release --features stealth

要求 Rust 1.75+。stealth 版本之所以要 cmake,是因为它要编译 BoringSSL 来做 TLS 指纹伪装——这个后面细说。

5.3 验证装好了

obscura fetch https://example.com --dump text --quiet

能吐出页面正文就成了。如果提示找不到 TLS 相关符号,说明你下的是非 stealth 包但用了 --stealth 参数——换 stealth 包,或者去掉参数(去掉后仍会做 tracker 拦截,只是没有 TLS 层伪装)。

5.4 放进容器里跑

单文件二进制最大的好处是容器镜像可以做得很小。用官方包做基础镜像,一条命令就能起一个可用的爬虫节点:

FROM debian:bookworm-slim
COPY obscura /usr/local/bin/obscura
EXPOSE 9222
ENTRYPOINT ["obscura", "serve", "--port", "9222", "--allow-private-network"]

镜像大小大概 90 MB,对比 Chrome 官方 headless 镜像(1.5 GB 以上)差了十几倍。如果你要起一批抓取 Worker,这个差别直接体现在拉镜像和扩缩容的速度上。

注意 --allow-private-network 那个参数:容器内如果要访问同网络的其他服务,必须加,否则会被 SSRF 保护拦掉。只在受信网络里这么做,别在公网暴露的实例上开。

六、CLI:三个命令覆盖八成场景

很多人装完 Obscura 会懵一下——它不是一个”打开就有界面”的软件,就是个命令行工具。但正因为这样,它可以被任何脚本调起来。

6.1 fetch:抓一个页面

# 只取可见文本
obscura fetch https://example.com --dump text

# 取渲染后的完整 DOM
obscura fetch https://example.com --dump html

# 取所有链接
obscura fetch https://example.com --dump links

# 取所有外部资源 + fetch/XHR 请求 URL(每行一个 JSON)
obscura fetch https://example.com --dump assets

# 取 Cookie(含 HttpOnly)
obscura fetch https://example.com --dump cookies

# 执行 JS 拿返回值
obscura fetch https://example.com --eval "document.title"

# 等动态内容加载完再取
obscura fetch https://news.ycombinator.com --wait-until networkidle0

# 截图
obscura fetch https://example.com --screenshot page.png

--dump assets 这个能力值得单独提一句:它把页面加载过程中所有请求的 URL 列出来,包括 JS 里的 fetch() 和 XHR。做接口逆向的时候,这一条命令顶得上开一轮 DevTools 面板。

6.2 scrape:批量并发

obscura scrape https://a.com https://b.com https://c.com 
  --concurrency 25 --format json --output result.json

--concurrency 25 是它敢给的默认量级。同样的数字放在 Chrome 上,16 GB 的机器直接爆内存;在 Obscura 上跑 25 个并发,内存占用大概 750 MB——一台普通笔记本就能扛。

6.3 serve:起一个 CDP 服务

obscura serve --port 9222

这一条是给 Playwright / Puppeteer 用的,下面单独讲。

七、零改动切换:Playwright 和 Puppeteer

这是 Obscura 最聪明的地方。它没有要求你学新 API,而是完整实现了 Chrome DevTools Protocol——Chrome 和自动化工具之间的那套协议。

7.1 Playwright 怎么接

from playwright.sync_api import sync_playwright

CDP = "ws://127.0.0.1:9222"

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(CDP)
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    print(page.inner_text("body"))
    browser.close()

注意这里用的是 connect_over_cdp,不是 launch。你不再让 Playwright 去启动浏览器,而是让它连上一个已经跑着的浏览器。这个模式本来就有,只是以前连的是 Chrome。

7.2 Puppeteer 怎么接

import puppeteer from "puppeteer-core";

const browser = await puppeteer.connect({
  browserWSEndpoint: "ws://127.0.0.1:9222/devtools/browser",
});
const page = await browser.newPage();
await page.goto("https://example.com");
console.log(await page.title());
await browser.disconnect();

7.3 哪些代码不用改,哪些会踩坑

不用改的:页面导航、元素选择器、文本提取、点击输入、Cookie 操作、请求拦截(v0.2.6 加强过)、多页面管理。这些是 CDP 的标准能力,Obscura 覆盖得比较完整。

可能踩坑的:

  • 截图:需要渲染能力,no-render 版做不了;
  • PDF 导出:同上,依赖完整渲染;
  • 视频/音频相关:媒体管线被砍了;
  • 复杂的 CSS 视觉断言:比如判断某个元素的实际渲染颜色,不保证跟 Chrome 一致;
  • Chrome 特有能力:扩展加载、DevTools 面板钩子这类,一概没有。

判断标准很简单:你要的东西如果只涉及”结构”和”数据”,换过来没问题;一旦涉及”看起来什么样”,就得仔细测。

7.4 迁移检查清单

从 Chrome 切过来之前,照着这张表过一遍,能省掉大部分返工:

  • 超时设置:Obscura 启动快,但你原来的 page.goto 超时是按 Chrome 加载速度定的,切换后可以调短,否则出错时白等;
  • 并发数:原来按 Chrome 内存算出来的并发上限,切换后能提一到两个数量级,但别一次拉到极限,先翻倍观察;
  • 等待策略:Chrome 上惯用的”睡 3 秒等渲染”,在 Obscura 上应该改成 wait_for_selector 这类显式等待,它不需要那么长时间;
  • UA 与视口:默认值可能不同,有反爬需求的站点要显式指定;
  • 截图与 PDF:确认你下的包支持渲染,no-render 版这两个功能不可用;
  • 错误处理:Obscura 报错信息的措辞跟 Chrome 不同,如果你的代码里靠匹配错误字符串做分支,要改;
  • Cookie 持久化:如果需要跨会话保持登录态,确认它把 Cookie 存在哪,别每次重启都丢。

这份清单不是吓唬人,是我实际迁移时踩过的顺序。前面四条最容易忘,后面三条属于”出问题才想起来”。

八、stealth:反检测做到什么程度

这是 Obscura 另一个卖点,也是最容易被误解的部分。

8.1 它改了什么

打开 --stealth 之后,以下这些特征会被处理:

检测点 默认行为 stealth 后
navigator.webdrivertrueundefined(引擎层直接不给)
指纹一致性各层可能矛盾GPU / 屏幕 / Canvas / 音频 / 电池统一成一套
TLS 指纹Rust HTTP 库特征伪装成真实 Chromium(BoringSSL)
原生函数可被 toString 识破patch 成原生形态
tracker 域名正常请求3520 个域名直接拦掉

其中「指纹一致性」是关键。很多反爬系统不只是查单个特征,而是交叉验证:你的 User-Agent 说是 Chrome 140,但 TLS ClientHello 长得像 Rust 库,WebGL 报的渲染器又是软件渲染——三个信号互相矛盾,直接判定为机器人。

Obscura 的做法是让这几层”说同一句话”,都指向同一个虚拟的 Chrome 身份。

8.2 它能过什么,不能过什么

根据公开实测和项目文档:

  • 能过:Cloudflare 的非交互式 Turnstile、Akamai BMP、PerimeterX、DataDome 的基础层、JA3/JA4 指纹检测、ALPN 顺序检测;
  • 不能过:需要人工交互的挑战(点选式 Turnstile、hCaptcha)、依赖 WebGPU / WebAssembly 怪异行为的深度指纹。

还有一点要注意:--stealth 的 TLS 层需要 stealth 编译版本。如果你下的是普通包,加了参数也只会做 tracker 拦截,TLS 指纹还是露的。这个坑很多人踩。

另外:反检测能力和合规是两件事。技术上能过某站的风控,不代表你可以随便抓——robots.txt、服务条款、数据性质这些,跟工具无关。

九、MCP:给 AI Agent 配一个便宜浏览器

Obscura 内置了 MCP(Model Context Protocol)服务。这意味着 Claude Desktop、Cursor 这类支持 MCP 的客户端,可以直接把它当工具调。

obscura mcp

配好之后,Agent 就有了”打开网页、读内容、点元素”的能力。这个场景为什么重要?因为 Agent 做网页任务时经常要开很多页面,如果每个页面都是 200 MB 的 Chrome,一台机器很快就撑不住了。

还有个细节设计挺实用:内置 Markdown 转换。它有一个自定义 CDP 域 LP.getMarkdown,能把 DOM 直接转成干净的 Markdown。对喂大模型来说,这比塞原始 HTML 省 token 得多——一个中等页面,HTML 可能两三万 token,转成 Markdown 往往只有一两千。

十、实测踩坑:我遇到的四个问题

10.1 SSRF 保护会拦住内网

Obscura 默认禁止访问私有 IP 和 localhost,这是防止 SSRF 的设计。但我第一次用它抓自己本地服务时,直接报错,找了半天才发现不是网络问题。

要抓内网,加参数:

obscura fetch http://192.168.1.100:8080 --allow-private-network

这个默认值是对的——大部分人的爬虫不需要碰内网,默认关掉更安全。但你要知道有这个东西,不然会以为是 bug。

10.2 单进程模型:一个页面崩,一片跟着崩

前面说过它砍掉了多进程沙箱。好处是省内存,代价是隔离性差。

具体表现:某个页面触发了 V8 的 OOM 或者引擎里的 panic,同进程里其他页面可能一起挂掉。Chrome 不会——它每个 tab 一个渲染进程,崩一个只影响一个。

应对办法:控制单实例的并发数,别一个进程扛几百个页面;重要任务加进程级监控,崩了自动拉起。

10.3 Cloudflare 不是万能的,也不是全不能

用真实站点测的时候会有落差:有些站点能过,有些过不去,而且同一站点不同路径的结果可能不一样(首页有防护,内页没有,或者反过来)。

我的经验是:先用 --stealth 试一次,拿到真实内容就继续;拿不到就换策略(降低频率、换 UA、用代理 IP 池),别在一个点上死磕。风控策略是动态的,今天能过不代表明天能过。

10.4 no-render 版本的功能边界比想象中小

no-render 版只有 39 MB,很吸引人。但用了之后发现:凡是需要”看到”的操作全做不了——截图不行、PDF 不行、基于坐标的点击不稳(因为没有布局后的精确位置)。

如果你的任务纯是”拿文本和链接”,no-render 完全够用;只要涉及交互或视觉,老老实实下完整版。

10.5 版本迭代快,跟着升要小心

这个坑不疼但很烦。项目半年迭代到 v0.2.6,几乎每周都有新提交,CDP 行为、CLI 参数、包名都改过。

我的建议是锁版本,不要用 latest 自动更新。生产环境写死版本号,升级前先在测试任务上跑一轮,确认脚本输出没变再切。尤其是依赖 CDP 细节的代码(自定义事件监听、请求拦截的时机),最容易在小版本之间出偏差。

还有一点:Release 里每个平台有四个包,升级时别下错——之前从完整版升级到 no-render 版,截图功能突然失效,排查了半天才发现是包选错了。

十一、什么时候该用,什么时候别用

场景 建议 原因
大批量抓取静态/半静态页面✅ 首选并发成本低一个数量级
AI Agent 的网页工具✅ 首选内置 MCP,省内存,启动快
CI / 容器里跑端到端测试✅ 适合镜像小,不需要装浏览器
需要截图 / 视觉回归测试⚠️ 谨慎渲染结果与 Chrome 有差异
依赖浏览器扩展❌ 不行没有扩展系统
强交互站点(复杂拖拽、富文本编辑)❌ 用 Chrome事件模型覆盖不全
需要长期稳定、不能崩的任务⚠️ 加监控单进程模型隔离性弱

如果你已经跑起来但卡在某个具体环节——比如 CDP 连不上、stealth 过不了某个站、并发拉上去之后进程不稳——可以把现象和报错发我看看:微信/手机 17712677007,说清楚用的是哪个包(完整版还是 no-render)、跑的什么场景,一般一两句话能定位。

如果拿不准,有个低成本的判断方式:先用你现有的 Playwright 脚本跑一遍,把 launch() 换成 connect_over_cdp() 指到 Obscura。跑通了就用,跑不通的用例继续留在 Chrome 上。混合部署完全不冲突,两个服务各占一个端口就行。

十二、写在最后

这类项目这两年出现得越来越多,背后是同一个趋势:为人类设计的基础设施,正在被改造成适合机器使用的形态。

浏览器是这样,Office 文档是这样,数据库访问也是这样。原来那套东西能用,但成本结构不适合新的使用方式——一个人一天开十个网页和程序一天开十万个网页,是完全不同的问题。

Obscura 的价值不在于”它比 Chrome 快十倍”这个营销数字,而在于它把每实例成本降到了一个新量级,于是原本做不到的事变得可行:Agent 可以并行开几十个页面查资料,爬虫可以在笔记本上跑几百并发,测试套件可以在免费的 CI 配额里跑完。

但它也确实是 0.x 版本的项目。半年三万多星,迭代很快(v0.2.6 一个版本就 117 个提交),意味着 API 和行为都还在变。用它做生产环境的核心组件之前,建议先在小规模任务上跑两周,把踩到的坑记录下来,再决定要不要扩大。

装的方式很简单,官方包解开就能跑,Windows 上不需要任何运行库。评论区可以说说你打算拿它做什么,或者已经在用的过程中遇到了什么问题。

✍️ 关于作者

IT 峰哥,互联网技术从业者。日常折腾服务器、虚拟化、数据库与企业级应用,关注开源系统的二次开发与基础设施落地。这里记录的是踩过的坑和验证过跑得通的方案,不是转述文档。

交流:微信 / 手机 17712677007(注明来意,看到会回)

文中方案如有疏漏,欢迎在评论区指正 · 技术问题欢迎交流

给TA打赏
共{{data.count}}人
人已打赏
默认

闲鱼捡漏怎么做到秒级响应?用 AI 编程搭一套自动监控提醒系统

2026-11-12 20:40:52

默认

ApkStart 怎么用?Windows 双击 APK 就跑安卓,比模拟器轻一个量级

2026-11-14 6:50:09

❯
购物车
优惠劵
搜索