{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
Har Derived Api Client
把站点的 XHR 录成 HAR,推导 HTTP 客户端。
Skill 元数据
| 来源 | 可选——使用 hermes skills install official/web-development/har-derived-api-client 安装 |
| 路径 | optional-skills/web-development/har-derived-api-client |
| 版本 | 0.1.0 |
| 作者 | Hermes Agent |
| 许可证 | MIT |
| 平台 | linux, macos, windows |
| 标签 | Browser, HAR, API, Reverse-Engineering, Playwright |
参考:完整 SKILL.md
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 skill 激活时 agent 所看到的指令内容。
HAR 推导 API 客户端
用一个真实浏览器驱动网站一次,同时把它的网络流量录到 HAR 文件里,然后把这个 HAR 提炼成该站点私有的 JSON API,这样你就能用普通 HTTP 直接调用它——比每次请求都用浏览器控制页面便宜得多、快得多。灵感:Jared Longster 的技巧,由 Dax(thdxr)推广。它捕获并重放;它不绕过认证、不解 CAPTCHA、也不破机器人检测——如果站点需要登录会话,你把它的请求头/cookie 带过去,而不是伪造它们。
脚本是标准库 + Playwright:捕获需要 Playwright,推导纯标准库,重放只需 requests/httpx(或 curl)。
覆盖每一条 Hermes 浏览器通路:默认本地 browser_navigate 后端,加上云/远程后端(Browserbase、Browser-Use、Firecrawl)和任何 /browser connect CDP 端点。有两个捕获脚本——一个针对你启动的浏览器,一个针对你通过 CDP 附加的浏览器——因为 HAR 录制在两种情况下工作方式不同(见运行方式)。
使用时机
- "给 <网站> 做个 CLI/客户端"——推导它的 API,而不是脚本化点击。
- "这个站点没有公开 API,但页面明显在取 JSON。"
- 你即将为同一个查询反复循环
browser_navigate——停下,一次性推导出端点。 - 逆向一个自动补全、搜索、feed 或结账 XHR。
- 你在云后端(Browserbase / Browser-Use / Firecrawl)或通过
/browser connect捕获了一个会话,想要这个 API 而不续租浏览器。
前提条件
- Playwright + 一个浏览器二进制(仅捕获步骤):
pip install playwright然后playwright install chromium --no-shell- (如果系统 Playwright 已在
~/.cache/ms-playwright下有浏览器,复用它。)
- 重放步骤用
requests或httpx(标准库urllib也行)。 - 无 API key。客户端需要的任何 key/token 都是 HAR 捕获到的那些。
- 对于 CDP 通路(
har_capture_cdp.py):一个可达的 CDP 端点。在 Hermes 上,运行/browser connect打印当前端点,或读 config 里的BROWSER_CDP_URL/browser.cdp_url。云后端把它暴露为cdpUrl/connectUrl。
运行方式
脚本在本 skill 的 scripts/ 下,通过 terminal 工具调用。按通路选捕获器——这是最容易踩坑的地方:
| 浏览器通路 | Hermes 如何到达它 | 捕获器 |
|---|---|---|
本地 browser_navigate(默认,agent-browser/Playwright) | 本地启动 | har_capture.py |
Camofox(设了 CAMOFOX_URL) | 本地 REST/CDP | 若暴露 CDP 用 har_capture_cdp.py,否则自己驱动 |
| Browserbase / Browser-Use / Firecrawl(云) | CDP(cdpUrl) | har_capture_cdp.py |
/browser connect <url> / BROWSER_CDP_URL | CDP | har_capture_cdp.py |
经验法则:如果 Hermes 启动了浏览器,用 har_capture.py;如果它通过 CDP 连接到一个浏览器,用 har_capture_cdp.py。 har_capture.py 用 Playwright 的 record_har_path,它只在本地拥有的 context 上工作。har_capture_cdp.py 用 connect_over_cdp() 附加,并从 page.on("request"/"response") 事件组装 HAR,因为 record_har_path 在已连接的浏览器上不可用。
然后,两条路径都一样:
har_to_client.py——把 HAR 过滤到 XHR/fetch/JSON,按端点分组,打印参数、请求头、请求体和重放提示(User-Agent / cookie / auth)。
路径按本 skill 目录解析。标准闭环:
# 1a. 捕获,本地浏览器(Hermes 启动的)
python3 scripts/har_capture.py "https://SITE/" out.har \
--action "fill:input[name=search]:my query" --action "sleep:3" --wait 2
# 1b. 捕获,CDP 浏览器(云后端或 /browser connect)
# 从 /browser connect 或 BROWSER_CDP_URL 拿端点
python3 scripts/har_capture_cdp.py "ws://HOST/devtools/browser/..." out.har \
--goto "https://SITE/" --action "fill:input[name=search]:my query" \
--action "sleep:3" --wait 2
# 2. 推导——从 HAR 里读出端点
python3 scripts/har_to_client.py out.har --host SITE --max-body 400
# 3. 重放——按打印出的端点写一个小客户端(见流程)
快速参考
har_capture.py <url> <out.har> [--wait S] [--headed] [--action SPEC ...]
action SPEC: fill:SELECTOR:TEXT | press:SELECTOR:KEY | click:SELECTOR
goto:URL | sleep:SECONDS (页面加载后按顺序运行)
当 Hermes 启动了浏览器时使用(本地 browser_navigate 默认)
har_capture_cdp.py <cdp_url> <out.har> [--goto URL] [--wait S] [--action SPEC ...]
相同的 action SPEC;附加到一个已存在的 CDP 浏览器,不关闭它
用于云后端(Browserbase/Browser-Use/Firecrawl)和 /browser connect
har_to_client.py <in.har> [--host SUBSTR] [--include-static] [--max-body N]
默认:只保留 XHR/fetch/JSON;--host 收窄到一个域名
每个端点打印:查询参数、非平凡请求头、请求体样本、
响应 status/content-type + 响应体样本
打印 "### Replay hints":浏览器 User-Agent、cookie/auth 是否存在
流程
- 按通路选捕获器(见运行方式表)。本地启动 →
har_capture.py;通过 CDP 到达 →har_capture_cdp.py。在 Hermes 上,当云/远程后端激活时,/browser connect会告诉你 CDP 端点。 - 找到交互。 用
browser_navigate(或--headed捕获)打开站点,看要往哪个选择器输入/点击,并在 devtools/network 里确认会发出一个 JSON XHR。 - 通过
terminal工具捕获 HAR。 安排--action顺序到达该请求:fill输入框,然后sleep足够长让防抖的 XHR 发出,并始终在末尾留--wait,让迟到的响应 flush。两个捕获器都内嵌响应体,所以推导出来的客户端看到真实的载荷形状。 - 用
har_to_client.py --host <domain>推导。 读出:方法、URL/路径模板(数字/UUID 段折叠为{id})、查询参数、请求体 JSON、以及### Replay hints块。 - 写客户端。 精确重现请求——相同方法、路径、查询参数、请求体。发送站点实际需要的请求头:至少从重放提示里复制 User-Agent。如果提示报告了 cookie 或 auth/token 头,也重新发送。
- 无头测试。 用
terminal工具运行客户端,确认它返回浏览器看到的相同数据。这就是回报:闭环里没有浏览器。 - (可选)包成 CLI——在推导出来的调用上套一个小
argparse脚本,例如search.py "frank herbert"。
完整示例(维基百科 search-title,已推导并线上重放):
import requests
r = requests.get(
"https://en.wikipedia.org/w/rest.php/v1/search/title",
params={"q": "frank herbert", "limit": 5},
headers={"accept": "application/json",
"User-Agent": "Mozilla/5.0 ... Chrome/131 Safari/537.36"}, # 来自 HAR
timeout=15,
)
for p in r.json()["pages"]:
print(p["title"], "-", p.get("description"))
常见陷阱
- 默认库 User-Agent 会被 403。 很多站点(维基百科、Cloudflare 前置的 API)拒绝
python-requests/x.y。始终发送重放提示里的浏览器 UA。这是浏览器成功而推导客户端失败的 #1 原因。 - 失败的
--action会在 HAR flush 之前中止——你拿不到文件。如果捕获在某个选择器上报错,这次运行什么都没产生;修选择器(用--headed观察)并重跑。不要去调试一个不存在的 HAR。 - 服务端渲染页面没有 XHR 可推导——
har_to_client.py打印 "No API-looking entries"。数据在 HTML 里;抓取它,或找到那个确实取 JSON 的交互。 - 防抖/自动补全 XHR 需要真实停顿。 在
fill后加--action "sleep:3";仅打字的话,HAR 关闭时请求还没发出。 - auth/会话端点需要捕获的
Cookie/Authorization头,而那些会过期。推导客户端的寿命取决于凭据;它 401 时重新捕获。HAR 包含活的机密——把out.har当敏感数据,推导后删除。 record_har_content="embed"会产生大 HAR。 用--max-body限制打印量;对媒体密集页面,文件本身可能很大。- 端点会变。 站点会无通知地改私有 API。客户端坏了时重跑 捕获→推导 闭环,而不是手改 URL。
- 选错捕获器 = 空/无 HAR。 在云/CDP 后端上用
har_capture.py什么都录不到(它启动了自己的本地浏览器,而不是你想要的那个)。har_capture_cdp.py需要端点;在 Hermes 上从/browser connect或BROWSER_CDP_URL拿。让捕获器匹配通路(运行方式表)。 - Headless-Chrome UA 是个弱特征。 本地/agent-browser 捕获会得到一个
HeadlessChrome/...User-Agent;有些站点嗅探 "Headless" token。云后端(Browserbase/Browser-Use)发送真实桌面 Chrome UA,所以从云捕获推导的客户端重放更可靠。如果一个 headless 推导的客户端在浏览器没被挡的地方 403,先把 "Headless" UA 换成普通 Chrome UA 字符串,再假设端点变了。 - CDP 捕获不关闭浏览器。
har_capture_cdp.py附加到一个它不拥有的浏览器并让它继续运行——这正是 Hermes 管理的云/远程会话所需要的。不要加关闭;让拥有它的后端去拆。
验证
对着一个无 API key 的线上站点做端到端证明:
python3 scripts/har_to_client.py ~/.hermes/cache/scratch/wiki.har --host wikipedia.org --max-body 200
期望推导出打印 GET https://en.wikipedia.org/w/rest.php/v1/search/title,带 q 和 limit 参数和一个 JSON pages 响应——然后用流程里的片段重放它,确认通过普通 HTTP 返回匹配的标题。