1. 为什么我放弃了让大模型读网页这条路
做批量网页抽取的人,迟早会撞上同一堵墙:Token 账单。单页抽取时感觉不到,一旦 URL 列表上到几百上千条,成本就随页数线性放大。更麻烦的是,模型读网页这件事本身不稳定——同一段 HTML,换个提示词、换个模型版本,抽出来的字段格式可能就不一样,你还得再写一层校验代码去兜底。
Browser4 走的是另一条路。它是一个 Rust CLI 加 Spring Boot 后端的浏览器引擎,通过 CDP 直驱 Chrome,把网页结构直接映射成可查询的数据表。核心能力叫 X-SQL:把 CSS 选择器命中的元素集合当成数据库的行,用一条 SQL 一次取出多个关联字段,顺带完成清洗、类型转换和排序。整个过程在本地完成,不经过任何 LLM,Token 消耗为零。
这篇文章适合谁:手上有一批结构相似的列表页要抽字段、字段需要做类型转换、还想跑定时任务的人。如果你只是偶尔读一两个页面,用模型对话就够了,没必要上这套。但如果你已经在为批量抽取的账单发愁,Browser4 的 X-SQL 值得花半小时跑通一次。
我实际安装并跑通了完整链路,包括一个 Intel Mac 用户必踩的安装坑——官方压根没出 darwin-x64 的预编译运行时包。下面从安装开始,一步步给到可复制的命令、SQL 语句和结果校验方法。
2. Browser4 是什么:Rust CLI 加 CDP 直驱的本地抽取引擎
先把架构讲清楚,不然后面配置容易懵。Browser4 的链路很短:
browser4-cli (Rust) ──MCP over HTTP──▶ browser4-rest (Kotlin/Spring) ──▶ PulsarWebDriver (Kotlin/CDP)CLI 是 Rust 写的,负责跟你交互;后端是 Kotlin/Spring 服务,默认监听 8182 端口;最底层通过 CDP 协议直接驱动 Chrome。三者之间用 MCP over HTTP 通信。你敲的每一条browser4-cli命令,本质是发给本地后端的一个请求。
它有三个核心模块,理解这三个就理解了它的定位:
| 模块 | 作用 |
|---|---|
| X-SQL | 把 CSS 选择器命中的元素集合当作数据库的行,用 SQL 一次取出多个关联字段,支持清洗、类型转换、排序 |
| PowerCSS | 在标准 CSS 上扩展:expr()伪类,用元素的计算视觉特征(宽高、位置、内容密度)做选择器,抗前端改版 |
| WebMiner | 本地机器学习管线,把结构未知、样式各异的批量页面自动聚类成结构化表格 |
X-SQL 是这篇的主角。传统方案抽一个列表页,要拆成三步:先写 Playwright 抓原始文本,再写 Python 或 Node.js 把£51.77这种带货币符号的字符串清洗成51.77,最后导入数据库写排序逻辑。三步三份代码三套依赖。X-SQL 把这三步压进一条 SELECT,清洗和类型转换由内置函数在查询时完成。
项目是 Apache 2.0 开源,GitHub 仓库platonai/Browser4,npm 包名browser4-cli。我实测时间点仓库约 1114 star,59 个 release,最新版本 v4.13.11,npm 已发 52 个版本。它不是要取代 Playwright——Playwright 在交互测试和复杂流程编排上依然是主力——而是在「批量抽取 + 零 Token」这个特定位置做差异化。
一个关键设计区分,理解了它才能用好这套工具:交互用snapshot,取数用htmlsnapshot。snapshot返回一次性的元素 ref(比如 e15、e16),DOM 一变 ref 就失效,所以 20 个商品页就得做 20 次快照;htmlsnapshot返回静态 DOM,你用 CSS 选择器定位,一个.price选择器能覆盖所有同类页面。前者适合点击填表,后者才是零 Token 批量抽取的正确入口。
3. 前置准备与可复制配置:Intel Mac 的绕路方案
官方 README 给的安装命令就两行:
npm install -g browser4-cli browser4-cli install第一步没问题。第二步在我这台 Intel Mac 上直接失败,报错信息是这样的:
Mirror 'aliyun-oss' speed test failed: HTTP 404 Not Found Mirror 'github' speed test timed out after 30s ... Download from 'github' failed: error sending request for url (https://github.com/platonai/Browser4/releases/latest/download/browser4-bundle-runtime-darwin-x64.tar.gz) Failed to download from all 2 mirror(s).我查了 v4.13.11 的 release assets,一共 11 个文件,运行时 bundle 只有三个平台:
browser4-bundle-runtime-darwin-arm64.tar.gz 115MB browser4-bundle-runtime-linux-x64.tar.gz 121MB browser4-bundle-runtime-windows-x64.zip 116MBdarwin-x64根本没有。官方镜像 aliyun-oss 的路径是 404,GitHub 直连又超时,两个原因叠加,browser4-cli install在 Intel Mac 上目前跑不通。这不是网络问题,是官方确实没出这个平台的包。
绕路方案:release 里有一个跨平台的 fat jar,直接用它起后端。后端要求 JDK 17+,本机如果只有 JDK 8 需要先装一个。
# 1) 建目录并拉跨平台 jar(官方 release 里就叫 Browser4.jar,约 89MB) mkdir -p ~/browser4-runtime curl -sL -o ~/browser4-runtime/Browser4.jar \ "https://gh-proxy.com/https://github.com/platonai/Browser4/releases/download/v4.13.11/Browser4.jar" # 2) 装 JDK 21(后端要求 17+) brew install openjdk@21 # 3) 设置 JAVA_HOME 并启动后端 export JAVA_HOME=/usr/local/opt/openjdk@21 export PATH="$JAVA_HOME/bin:$PATH" java -jar ~/browser4-runtime/Browser4.jar &后端起来后,确认 CLI 和后端是否连通:
browser4-cli status正常输出:
Browser4 Status =============== CLI version: 0.1.28 Server URL: http://localhost:8182 Server health: UPServer health: UP就表示链路通了。这时后端日志里会打印一批工具注册信息,大约 40 个 MCP 工具(navigate / reload / click / fill / snapshot / page_source 等)全部加载成功。
如果你用的是 Apple Silicon Mac 或 Linux x64,直接走browser4-cli install就行,不用这套绕路。只有 Intel Mac 需要手动起 jar。
关于配置,Browser4 的 CLI 配置走环境变量和命令行参数,没有复杂的 TOML 文件。如果你要把它接到别的工具里,需要记住三件套:Base URL 是http://localhost:8182,Key 在本地模式下不需要(后端跑在本机),Model ID 不适用——因为 X-SQL 路径根本不调模型。这一点和接大模型 API 完全不同,别把两套配置混在一起。
4. 一条 SQL 抽 8 本书:完整执行与结果校验
核心演示就是这条查询。我把它写进一个 SQL 文件,注意:SQL 文件不能带注释,这是第一个坑,后面会细说。
新建query.sql:
SELECT DOM_FIRST_TEXT(DOM, 'h3') AS title, DOM_FIRST_FLOAT(DOM, '.price_color', 0.0) AS price, DOM_FIRST_ATTR(DOM, 'h3 a', 'href') AS path, DOM_FIRST_ATTR(DOM, '.star-rating', 'class') AS rating FROM LOAD_AND_SELECT('https://books.toscrape.com/', '.product_pod') ORDER BY price ASC LIMIT 8这条 SQL 里有几个关键点。LOAD_AND_SELECT(url, selector)是数据源函数,第一个参数是目标 URL,第二个参数是行选择器——.product_pod命中页面上每个商品卡片,每个卡片就是结果集里的一行。DOM_FIRST_TEXT(DOM, 'h3')取每行内第一个 h3 的文本作为书名。DOM_FIRST_FLOAT(DOM, '.price_color', 0.0)取价格文本并转成浮点数,第三个参数是转换失败时的默认值。DOM_FIRST_ATTR取属性值,用来拿链接和评分 class。
执行分两步:
browser4-cli open https://books.toscrape.com/ browser4-cli htmlsnapshot query --sql @query.sql真实返回(截取部分):
{"statusCode":200,"pageStatusCode":200,"pageContentBytes":63479,"isDone":true, "resultSet":[ {"title":"Starving Hearts (Triangular Trade ...","price":"13.99","rating":"star-rating Two"}, {"title":"Set Me Free","price":"17.46","rating":"star-rating Five"}, {"title":"The Coming Woman: A ...","price":"17.93","rating":"star-rating Three"}, {"title":"Shakespeare's Sonnets","price":"20.66","rating":"star-rating Four"}, {"title":"The Boys in the ...","price":"22.6","rating":"star-rating Four"}, {"title":"The Requiem Red","price":"22.65","rating":"star-rating One"}, {"title":"Olio","price":"23.88","rating":"star-rating One"}, {"title":"The Dirty Little Secrets ...","price":"33.34","rating":"star-rating Four"} ]}校验结果,看四个点。第一,statusCode和pageStatusCode都是 200,说明页面抓取成功。第二,resultSet有 8 条,LIMIT 8生效。第三,价格是升序的,13.99 到 33.34,ORDER BY price ASC生效。第四,价格字段是纯数字字符串,£51.77里的货币符号已经被DOM_FIRST_FLOAT清洗掉了。
这条查询一次完成了四件事:取书名、取价格并把带货币符号的字符串转成浮点数、取链接路径、取评分 class,然后按价格升序取前 8 条。全程没有调用任何 LLM,Token 消耗为 0。
一个容易忽略的细节:官方文档示例的输出价格是 10.17 / 10.64 / 10.84,我跑出的是 13.99 / 17.46。这不是 Bug——books.toscrape.com 每次访问都随机生成商品页,这是站点本身的设计。所以你的结果和我的不一样是正常的,只要字段结构对、排序对,就说明查询正确。
批量场景下,把 URL 列表写进文件,用 crawl 或 swarm 跑:
# 单机批量,输出 CSV browser4-cli crawl --seed-file urls.txt --depth 0 --sql @query.sql --format csv # Swarm 模式,多浏览器上下文并发 browser4-cli swarm create --max-browser-contexts 8 browser4-cli swarm query --seed-file urls_10k.txt --sql @query.sql browser4-cli swarm result <task-id>--depth 0表示只抽种子 URL 本身,不跟进链接。--format csv直接输出 CSV,省掉自己写导出代码。Swarm 模式的并发数由--max-browser-contexts控制,8 表示同时开 8 个浏览器上下文。
5. 常见报错排查:从 401 到 SQL 500 的对照表
这一节是我实际踩过的坑,按报错现象对照解法。
| 问题现象 | 原因 | 解法 |
|---|---|---|
browser4-cli install报 asset not found | Intel Mac 无官方 runtime bundle | 用 release 里的 Browser4.jar,java -jar启动 |
| 官方镜像 404 | aliyun-oss 路径返回 404 | 走 gh-proxy 代理下载 jar |
| 后端启动失败 | JDK 版本低于 17 | brew install openjdk@21并设置 JAVA_HOME |
SQL 返回 500Only select statements are supported | SQL 文件带--注释 | SQL 必须写成纯 SELECT,不加注释 |
Server health: DOWN | 后端没起来或端口被占 | 检查 8182 端口,重启 jar |
local proxy failed | 本地代理配置干扰了 CDP 连接 | 清掉 HTTP_PROXY / HTTPS_PROXY 环境变量 |
返回reading choices相关错误 | 请求体格式不对 | 确认--sql @file.sql的 @ 前缀没漏 |
第四个是我一开始就栽进去的。照抄官方示例的注释头,第一次执行直接 500:
{"statusCode":500,"message":"Only select statements are supported"}错误信息很明确,但得知道原因才能绕过去——解析器把--注释当成了非 SELECT 语句。把注释删掉,纯 SELECT 就正常了。
关于local proxy failed:如果你本机设了 HTTP_PROXY 之类的环境变量,CDP 连接可能被劫持到代理上,导致连不上 Chrome。跑 Browser4 前先unset HTTP_PROXY HTTPS_PROXY,或者在一个干净的环境里跑。
关于 401:本地模式下后端不校验 Key,正常不会出 401。如果你把后端暴露到局域网或改了配置加了鉴权,才会遇到。这时检查 CLI 的请求头里有没有带上正确的凭证。但绝大多数本地使用场景不会碰到这个。
还有一个隐蔽的坑:browser4-cli open和htmlsnapshot query之间,会话要保持。如果你用了命名会话-s work,查询时也要带同样的-s work,否则查的是另一个会话的页面,结果为空。
browser4-cli -s work open https://books.toscrape.com/ browser4-cli -s work htmlsnapshot query --sql @query.sql会话隔离是个好设计,多任务并行时互不干扰,但前提是 open 和 query 用同一个会话名。
6. 命令速查与场景判断
把常用命令整理成速查表,方便你复制。
# === 启动 === java -jar ~/browser4-runtime/Browser4.jar & browser4-cli status # === 会话 === browser4-cli open https://example.com browser4-cli open --headed https://example.com browser4-cli -s work open https://site.com browser4-cli list # === 交互(refs 来自 snapshot)=== browser4-cli snapshot -i --boxes browser4-cli click e15 browser4-cli fill e16 "text" --submit # === 静态抽取(零 LLM Token)=== browser4-cli htmlsnapshot browser4-cli htmlsnapshot get text "h1" browser4-cli htmlsnapshot query --sql @query.sql # === 批量 === browser4-cli crawl --seed-file urls.txt --depth 0 --sql @q.sql --format csv browser4-cli swarm create --max-browser-contexts 8 browser4-cli swarm query --seed-file urls_10k.txt --sql @q.sql browser4-cli swarm result <task-id> # === 定时 === browser4-cli loop -i 3600 -- eval 'document.querySelector(".price")?.textContent'loop -i 3600表示每 3600 秒跑一次,适合持续监控某类页面。eval后面跟一段 JS,用来做轻量的自定义取值。
场景判断,我的实测结论:
适合用的场景——批量 URL 列表抽取、结构已知或半已知的列表页、字段需要清洗和类型转换、跑定时任务持续监控。这类任务重复度高、结构稳定,本地 SQL 路径的收益远大于 LLM。
不适合的场景——多步交互流程(登录、填表、翻页点击)、需要理解语义的开放式任务、单次性的页面理解。这些还是走 LLM 驱动的浏览器自动化框架更合适。
需要说明的是,Swarm 模式的「单机每天 10-20 万页」是项目方自述的初步性能测试结果,官方也注明正式 benchmark 还在准备中,我没有验证这个数字。WebMiner 和 PowerCSS 的:expr()我这次也没有实测——WebMiner 同样需要 JDK 17+ 全套环境,PowerCSS 需要构造特定页面。
如果你要长期跑批量抽取任务,把 Browser4 的本地抽取和 TaoToken 的模型能力配合起来是个思路:结构化的字段用 X-SQL 本地抽,零 Token;遇到需要语义判断的字段(比如判断评论情感、归类商品类目),再把那一小部分文本送去模型处理。这样 Token 只花在真正需要理解的地方,而不是浪费在解析 HTML 结构上。模型对话入口在 https://taotoken.net/api ,接入文档在 https://taotoken.net/doc ,需要长期编码或 Agent 场景可以看 Coding Plan:https://taotoken.net/coding-plan 。
回到 Browser4 本身。它的核心价值不是「又一个浏览器自动化框架」,而是把「抽取 + 清洗 + 排序」这三步压进一条 SQL,全程走本地数据库引擎,不产生 Token 费用。我实测的 books.toscrape.com 示例确实跑通了,四字段一查询、自动类型转换、ORDER BY 生效,返回 HTTP 200 和 8 行结果。
如果你的场景是批量结构化抽取,值得一试。如果你的 Mac 是 Intel 芯片,记住走 Browser4.jar 这条路,别在browser4-cli install上浪费时间——目前官方确实没出 darwin-x64 的运行时包。跑通之后,把query.sql换成你自己的选择器和字段,就能直接复用到目标站点上。