用途
Opensteer 用于浏览器自动化、网页抓取和结构化数据提取。它通过 CLI 和 TypeScript SDK 提供持久化浏览器自动化能力,在多次调用之间保持浏览器会话,并缓存已解析的元素路径,以便确定性重放。
主要能力
- 持久化浏览器会话:跨调用保持会话状态。
- 选择器缓存与重放:通过
--description缓存元素路径,重放时无需重新解析。 - 结构化数据提取:使用
extract配合 schema 或缓存的 description 输出 JSON。 - 多种快照模式:
action(可交互元素)、extraction(面向数据的扁平化 HTML)、clickable(仅可点击元素)、scrollable(仅可滚动容器)、full(原始 HTML,仅用于调试)。 - 元素定位方式(按优先级):快照计数器、缓存 description、CSS 选择器。
- 浏览器连接:默认使用沙箱中的全新 Chromium;也可通过
--connect-url连接到已启用 CDP 的现有浏览器。
使用方式
官方强调:当存在 Opensteer 方法时,不要使用原生 Playwright。Opensteer 方法会自动处理等待、元素解析和选择器缓存。唯一允许使用 opensteer.page.evaluate() 的场景是调用 fetch() 进行基于 API 的提取(当站点有内部 REST/GraphQL 端点时)。
默认工作流:始终先用 CLI 探索,仅在用户要求时才编写脚本。
- 用 CLI 探索:打开页面、快照、交互。
- 缓存选择器:用
--description重跑动作以缓存元素路径。 - 缓存提取:对抓取器将访问的每种页面类型运行带
--description的 extract。 - 生成脚本:在 TypeScript 中使用缓存的 description。
CLI 与 SDK 通过名称共享缓存:opensteer open 的 --name 定义缓存命名空间,SDK 中 new Opensteer({ name: "..." }) 读取同一缓存,二者必须匹配。
CLI 探索示例流程:
# 1. 每个 shell 设置一次会话
export OPENSTEER_SESSION=my-session
# 2. 带命名空间打开页面
opensteer open https://example.com/products --name "product-scraper"
# 3. 快照
opensteer snapshot action # 带计数器的可交互元素
opensteer snapshot extraction # 带计数器的面向数据的 HTML
# 4. 使用快照中的计数器交互
opensteer click 3
opensteer input 5 "laptop" --pressEnter
# 5. 用 --description 缓存动作以便重放
opensteer click 3 --description "the products link"
opensteer input 5 "laptop" --pressEnter --description "the search input"
# 6. 提取数据
opensteer snapshot extraction
opensteer extract '{"products":[{"name":{"element":11},"price":{"element":12}},{"name":{"element":25},"price":{"element":26}}]}' \
--description "product listing with name and price"
# 7. 为所有页面类型缓存提取
opensteer click 11 --description "first product link"
opensteer snapshot extraction
opensteer extract '{"title":{"element":3},"price":{"element":7}}' \
--description "product detail page"
# 8. 完成后关闭
opensteer close
关键规则:
- 在 open 时设置
--name定义缓存命名空间。 - 明确指定快照模式:
action(交互)或extraction(数据)。 snapshot extraction显示结构,extract生成 JSON——不要手动解析快照 HTML。- 使用
--description缓存选择器以便重放(一个字符差异即缓存未命中)。 - 对数组,在 schema 中包含所有条目——Opensteer 缓存结构模式,重放时找到所有匹配项。
open执行原始page.goto();后续页面使用navigate(包含稳定性等待)。- 导航或页面重大变化后重新快照。
编写抓取脚本前,先阅读 sdk-reference.md 获取确切的方法签名。
脚本模板:
import { Opensteer } from "opensteer";
async function run() {
const opensteer = new Opensteer({
name: "product-scraper", // 必须与 CLI 探索时的 --name 匹配
storage: { rootDir: process.cwd() },
});
await opensteer.launch({ headless: false });
try {
await opensteer.goto("https://example.com/products");
await opensteer.input({
text: "laptop",
description: "the search input", // 与 CLI --description 完全匹配
});
// 缓存存在时使用带 description 的 extract——无需 schema
const data = await opensteer.extract({
description: "product listing with name and price",
});
console.log(JSON.stringify(data, null, 2));
} finally {
await opensteer.close();
}
}
run().catch((err) => {
console.error(err);
process.exit(1);
});
脚本规则:
- 不使用顶层 await——包裹在
async function run()中并配合run().catch(...)。 - 默认
headless: false(许多站点会屏蔽无头模式)。 - 所有交互和提取使用缓存的 description 字符串。
- 不要在 SDK 动作前添加 wait 调用——它们内部处理等待。
- 仅在页面跳转或确认 SPA 内容加载时使用
opensteer.waitForText("literal text")或page.waitForSelector("css")。 - 运行方式:
npx tsx scraper.ts。
浏览器连接:
- 沙箱(默认):
opensteer open <url>——全新 Chromium,无用户会话。 - 连接(现有浏览器):
opensteer open --connect-url http://localhost:9222——附加到运行中的启用 CDP 的浏览器。验证 CDP:curl -s http://127.0.0.1:9222/json/version。
元素定位(优先级顺序):
- 计数器(来自快照):
click 5——快速,需要最新快照。 - Description(缓存):
click --description "the submit button"——可重放。 - CSS 选择器:
click --selector "#btn"——明确但脆弱。
适用场景
- 需要跨多次调用保持浏览器会话的网页抓取任务。
- 需要确定性重放的结构化数据提取。
- 对同一模板的多个页面进行批量抓取。
- 需要与现有 CDP 浏览器会话配合的自动化场景。
限制
- 官方明确要求:存在 Opensteer 方法时不要使用原生 Playwright;
page.evaluate()仅限用于调用fetch()进行 API 提取。 - 缓存 description 必须精确匹配,一个字符差异即缓存未命中。
- 计数器定位需要最新快照。
- CSS 选择器定位明确但脆弱。
- 调试时不要用
page.evaluate()+querySelectorAll替换opensteer.extract();应修复时序、缓存或障碍问题。 - 关于收费、价格、平台支持范围等具体信息,资料中暂未提供。
调试顺序
当抓取器产生错误或缺失数据时,按以下顺序诊断:
- 时序——SPA 内容未渲染。在提取前添加
waitForSelector或waitForText。 - 缺失缓存——CLI 探索时忘记为某页面类型缓存提取。
- 障碍——Cookie 横幅、模态框或登录墙阻挡目标。
- 缺失数据——某些页面确实缺少某些字段。用 null 检查处理。
参考文档
- CLI 命令:
cli-reference.md - SDK API:
sdk-reference.md - 完整示例:
examples.md