用 REA 追踪网站 API 调用:浏览器捕获、HAR 与前端 bundle
本页目录
在网页上点一个按钮,页面加载了些东西。到底发了哪个请求、返回了什么、是哪一行 JavaScript 发出的?DevTools 能在当前会话里回答这些问题,但关掉标签页答案就没了,而且 AI agent 看不到你的 DevTools 面板。
REA(morluto 维护的开源项目)给 agent 提供了一组浏览器和抓包工具,把这些问题的答案沉淀为 Evidence。它连接你自己启动的浏览器,在你指定的时间窗口里记录页面行为;离线读取保存好的 HAR 和 Fiddler SAZ 文件;还能借助 source map,把捕获到的脚本位置映射回原始代码。
适合谁读: 想把自家网站(或公开演示站)与后端的交互方式记录清楚的前端/全栈开发者、测试工程师和安全方向的学习者。
你会学到:
- 让 REA 连接一个独立的 Chrome 配置,并选中目标页面
- 截图,并记录一次用户操作
- 读出请求、发起者(initiator)和响应结构
- 导出捕获到的脚本,找到调用 API 的代码
- 离线分析 HAR 和 Fiddler SAZ,包括 API 面视图
- 用 source map 把压缩后的位置映射回源码
只分析你有权分析的网站。 本文示例使用 REA 的公开 Notes 演示页 https://rea.tools/examples/notes-web/,以及代表你自己站点的占位地址。观测任何其他网站之前,先阅读它的服务条款,需要时取得许可。不要用这些工具绕过登录、反爬或限流。参见 REA 的 DISCLAIMER.md。
前置条件
- 已安装 REA。
npx rea-agents setup把 MCP 服务注册到你的 agent;npm install --global rea-agents安装rea命令。下面每条命令也都可以写成npx -y rea-agents@latest <command>。参见 安装 REA 并接入 Claude Code / Cursor / Codex。 - 一个 Chrome 系浏览器。 REA 通过 Chrome DevTools Protocol(CDP)连接。被动检查时,它不会启动、操控或关闭浏览器。
- 不需要原生分析引擎。 浏览器捕获和 JavaScript 分析都用不到 Ghidra、Hopper 或 IDA。
- 如果要读抓包文件: 浏览器或代理导出的 HAR 1.2,或 Fiddler Everywhere 保存的
.saz。
第 1 步:用远程调试端口启动一个独立浏览器
# Linux
google-chrome \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/rea-notes-browser \
https://rea.tools/examples/notes-web/
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/rea-notes-browser \
https://rea.tools/examples/notes-web/
为什么要单独的配置目录: CDP 能深入访问整个浏览器配置,包括 cookie 和存储。用一个一次性的 --user-data-dir,可以把你日常的登录状态隔离在调查之外。不要把调试端口暴露在回环地址以外的网卡上,用完自己关掉浏览器。
Linux 上如果桌面密钥环处于锁定状态,Chrome 可能在第一次导航前卡住。对专用配置可以加上 --password-store=basic 来避开。
演示跑通之后,把 URL 换成你自己站点的页面,比如本地开发服务器 http://127.0.0.1:3000。
第 2 步:列出页面目标
rea list-browser-targets http://127.0.0.1:9222 --json
为什么这样做: target ID 只在当前这个浏览器实例里有效,所以每次捕获都从这里开始。找到 URL 对应你页面的那个 target,复制它的 ID。端点要写 127.0.0.1,不要写 localhost,REA 只接受字面量形式的回环 CDP 端点。
通过 agent 操作时,把端点和页面 URL 告诉它即可。它会用 {"cdp_endpoint": "http://127.0.0.1:9222"} 调用 list_browser_targets,自己挑选 target。
第 3 步:截图
rea capture-web-screenshot http://127.0.0.1:9222 TARGET_ID --json > before.json
结果是可见视口的一张 PNG,按内容寻址,直接内联返回。截图适合在一次操作前后各留一份记录,也能用来确认 agent 连上的确实是你想要的页面。在本地比较两张截图(不走 OCR,也不调用外部服务):
# The comparison takes the screenshot artifact, not the whole Evidence record.
jq '.normalized_result.artifact' before.json > before-artifact.json
jq '.normalized_result.artifact' after.json > after-artifact.json
rea compare-web-screenshots before-artifact.json after-artifact.json --json
请传文件路径而不是内联 JSON,因为内嵌的 PNG 字节可能超过 shell 的参数长度上限。
第 4 步:记录一次操作
开启一个观测窗口,在窗口期间到浏览器里点击 Export notes:
rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
--observation-ms 10000 \
--include-accessibility-text \
--include-json-body-shapes --json > export-click.json
每个参数的作用:
--observation-ms 10000给你 10 秒去操作,默认窗口只有 500 ms。网络和控制台的记录从 REA 连上之后才开始,窗口之前发出的请求不在结果里。没记录到,不代表没发生。--include-accessibility-text保留按钮名称和标签,方便你和 agent 把请求和你点的那个控件对应起来。--include-json-body-shapes记录 JSON 响应的结构(属性路径、类型、数量),不保留具体值。
REA 的 浏览器指南 在 Notes 演示页上记录过同一个操作。用 REA 4.1.0 观测到的结果是:
| 捕获到的事实 | 值 |
|---|---|
| 请求 | GET /examples/notes-web/notes.json |
| 响应 | 200 · application/json |
| 请求发起者 | export.js · script |
| 响应结构 | notes[],含 id 和 title 字段 |
网络观测保留方法、状态码、MIME 类型、大小、发起者调用栈位置和完整 URL。cookie 和 authorization 头永远不会被保留;DOM 快照只保留节点名和属性名,不保留文本和属性值。如果捕获内容可能涉及真实账号,先读一遍 数据最小化说明。
如果你关心的是页面跳转、重定向和 SPA 路由变化,而不是资源请求,用 rea observe-web-session http://127.0.0.1:9222 TARGET_ID --observation-ms 10000 --json。
第 5 步:找到发出请求的代码
initiator 告诉你是哪个脚本发起了请求,接下来就去读这个脚本。
5.1 捕获并导出脚本
rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
--include-script-sources --json > scripts-capture.json
rea export-web-scripts /absolute/path/scripts-capture.json \
/absolute/path/exported-scripts --json
rea analyze-javascript-application /absolute/path/exported-scripts/files --json > app-evidence.json
为什么这样做: export-web-scripts 把捕获到的源码和一个经过校验的 manifest.json 写进一个新目录,目标目录必须事先不存在。之后本地 JavaScript 分析器会盘点模块和 import,全程不执行代码。导出过程不会抓取任何 URL,所以你分析的正是浏览器当时拿到的内容。
在 Notes 演示里,捕获到的 export.js 包含了网络捕获看到的那次调用:
const response = await fetch("./notes.json");
const { notes } = await response.json();
网络 Evidence 证明这次点击请求了 notes.json,脚本源码说明响应如何变成 CSV。这两件事请分开记录,任何一方都不能单独证明另一方。
5.2 在大型应用里拿到精确调用点
真实应用里,initiator 往往指向一个很大的 bundle。observe-web-execution 会记录请求发起者、推导出的调用点和脚本 ID,同时记录同一窗口内的 V8 精确覆盖率:
rea observe-web-execution http://127.0.0.1:9222 TARGET_ID \
--observation-ms 10000 --json > execution.json
等 stderr 上出现 armed 进度事件之后再点击。注意,精确覆盖率运行期间会改变时序和 JIT 行为。想看按钮本身注册了哪些监听器:
rea inspect-web-event-listeners http://127.0.0.1:9222 TARGET_ID '#export' --json
它返回直接注册在该节点上的监听器及回调位置。挂在祖先节点上的委托监听器和框架内部的注册表不会被列出,所以 React 或 Vue 的按钮返回空结果是正常的。
5.3 静态 bundle 分析
rea analyze-web-bundle http://127.0.0.1:9222 TARGET_ID --json
analyze-web-bundle 不执行脚本,只做解析,返回 chunk 之间的关系、路由和端点候选、第三方库指纹。端点候选能帮你找到这次操作没触发的 API 调用。但它们只是候选,不是观测结果:代码里出现一个像 /api/v1/notes 的字符串,不代表真的有人调用它。
在你自己的站点上,还可以加 --fetch-source-maps,让它从同源地址抓取 source map。
5.4 把压缩后的位置映射回源码
如果你手上有自己构建产物的 source map,可以把生成代码里的位置映射到原始文件:
rea trace-web-source-location \
/absolute/path/exported-scripts/manifest.json 0 \
/absolute/path/app.js.map 'https://your-site.example/assets/app.js.map' \
1 25 --json
六个位置参数依次是:manifest、从 0 开始的脚本序号、本地 map 路径、map 的 URL 上下文、从 1 开始的生成行号、从 0 开始的 UTF-16 列号。URL 只用来解析 sources,不会去抓取。CDP 给出的 initiator 行号从 0 开始,所以对外部脚本要把行号加 1 再填进来。内联脚本的换算方法见 web runtime 文档。
第 6 步:离线读取 HAR
不是每次都能实时捕获:可能是同事发来一个 HAR,也可能是代理录下的会话。REA 离线读取 HAR 1.2,不会请求其中记录的 URL,也不会启动浏览器或代理。
从 Chrome 导出的方法:打开 DevTools,在 Network 面板里录下操作,然后用面板自带的 HAR 导出功能。HAR 里可能包含 cookie 和 token,浏览器如果提供脱敏导出,优先选它。
rea inspect-web-network-capture /absolute/path/session.har har --json > har-evidence.json
rea inspect-web-network-capture /absolute/path/session.har har --record 2 --record 0 --json
--record 按你给的顺序挑选从 0 开始编号的条目。REA 会先校验所有条目再挑选,所以文件别处有格式错误的条目,照样会报出来。header 和 cookie 里的认证信息会按结构自动排除。如果还要排除某个特定的敏感值,比如出现在请求体里的测试 API key,可以重复使用 --sensitive-value:
rea inspect-web-network-capture /absolute/path/session.har har \
--sensitive-value 'test-key-123' --json
把请求归并成 API 面
想快速列出 HAR 里有哪些端点,可以用 api-surface 视图投影保存好的 Evidence:
jq -c '{source: {kind: "inline", evidence: .}, view: {kind: "api-surface", offset: 0, limit: 50}}' \
har-evidence.json > har-api-view.json
rea inspect-analysis-view har-api-view.json
这个视图按方法、origin 和路径模板分组。纯数字段、UUID、长十六进制串和 ISO 日期分别替换成 {numeric}、{uuid}、{hex}、{iso-date}。于是 /notes/41 和 /notes/42 会合并成一组 GET /notes/{numeric},并列出两个实际观测到的值。查询串、header 和请求体不会被复制进分组。这个视图不判断哪个后端处理函数负责某条路由;如果上游检查用过 --sensitive-value,它会直接拒绝,因为它没法拿未保留的声明来核对自己的输出。
通过 MCP 时,agent 可以先用 detail: "summary" 调用 inspect_web_network_capture,再用 inspect_analysis_view 分页读取记录,避免大文件塞进单次响应。
第 7 步:读取 Fiddler Everywhere 的 SAZ 文件
Fiddler Everywhere 把会话保存为 Session Archive Zip。REA 可以直接读取,不需要安装 Fiddler,也不用先转成 HAR:
rea inspect-web-network-capture /absolute/path/session.saz saz --json
rea inspect-web-network-capture /absolute/path/session.saz saz --record 0 --json
REA 会把压缩包里每个会话的请求、响应和元数据成员配对。序号按 ZIP 目录顺序,不是 Fiddler 的会话编号。请求体按原样保留,不做分块合并也不解压。API 面视图只支持 HAR,所以 SAZ 要么逐条分页查看,要么在 Fiddler 里 Export > HTTPArchive 导出 HAR(确认 log.version 为 "1.2"),同时保留原始 SAZ。
Fiddler Everywhere 自己也有一个面向实时流量的 MCP 服务。REA 不封装它,两者都想用就分别接入。参见 Fiddler Everywhere 抓包与 MCP。
第 8 步:交给 agent 串起来
熟悉这些环节后,一句提示词就能覆盖整个流程:
Use REA to inspect the page at http://127.0.0.1:9222 while I click Export notes.
Tell me which request it makes, which script initiates it, and show the code
that builds the CSV. Cite the Evidence ID for each fact.
核对答案的方式和其他 REA 结果一样:请求应来自 inspect_web_page 的网络 Evidence,发起者来自同一条记录,代码来自捕获或导出的脚本源码。如果 agent 提到的某个端点只出现在 analyze_web_bundle 的候选里,就追问它:这是观测到的,还是只在代码里看到的?
常见问题和排错
list-browser-targets 报错或返回空。 确认 Chrome 带着 --remote-debugging-port=9222 启动,同一个配置目录没有被另一个 Chrome 实例占用,并且你用的是 http://127.0.0.1:9222。localhost、HTTPS 端点和带路径的地址都会被拒绝。
我要的请求没出现。 要么它发生在窗口开启之前,要么它发往了默认范围之外的 origin。捕获默认只看页面当前的 origin;发到独立 API 域名的请求会被过滤,除非你把那个 origin 加进 allowed_origins(CLI 上是 --allowed-origins)。
报 target_changed。 页面在最终捕获期间发生了跳转,REA 丢弃了混杂的结果。等跳转完成后重新开窗口。
export-web-scripts 提示没有可导出的源码。 捕获时没带上脚本内容。加 --include-script-sources 重新捕获。
HAR 校验失败。 只支持 HAR 1.2,重复的 JSON 键会被拒绝。检查 log.version;如果文件被手工改过,从原工具重新导出。
大文件触发资源上限。 HAR 和 SAZ 检查的输入预算是 32 MiB。只导出覆盖目标操作的那一小段,完整文件留作出处记录。
下一步
- 分析基于同一技术栈的桌面应用:用 REA 分析 Electron 应用。
- 跨版本保存和对比捕获结果:Evidence、快照与版本对比。
- 把同样的 Evidence 习惯用到二进制上:用 REA 和 AI agent 解 CTF 逆向题。
- 完整阅读 浏览器观测 和 网络抓包 参考文档。