Scrapewright将爬虫从脚本转为显式状态机图,AI只参与生成阶段,运行时不消耗Token不限速,支持本地HTTP API调用。
你写过的每一个爬虫都是谎言。你把它写成脚本——打开页面、查找元素、点击、提取——但一旦它遇到真实网站,就变成了别的东西:一个隐藏的状态机,包含重试、等待、回退和错误分支,都用 try/except 和"感觉"来表达。Scrapewright 把状态机变成显式模型,这改变了爬虫失败、修复和生成的方式。
Scrapewright 服务是一个由命名步骤组成的有向图。每个步骤是一段小脚本——由 LLM 生成、可手动编辑——带有显式的控制流边:
{
"id": "wait_results",
"name": "Wait for search results",
"script": "return { done: $count('div.result') > 0 }",
"onSuccess": "extract_list",
"onFailure": "TERMINATE",
"maxIterations": 20
}
几个关键字段:
onSuccess —— 当前步骤成功后的下一步(内容就绪、数据已提取),或者 TERMINATE。
onFailure —— 步骤失败或放弃时控制权转移的目标:条件为 false、重试预算耗尽、或者脚本返回 { failed: true } / { error: '...' }。
condition —— 一个可选的 JS 表达式,在目标标签页中求值;为 false 时跳过并走 onFailure 分支。自由分支。
maxIterations —— 1(默认)是一个普通步骤。大于 1 则使该步骤具备轮询/重试语义。
最后这个字段是我最欣赏的设计决策。轮询不是隐藏在代码深处的特殊 API 或 while 循环——它只是节点的一个属性。一个等待步骤返回 { done: false } 表示"还没准备好,再跑一次";编排器最多重试 maxIterations 次,然后走 onFailure。对于任何有后向边指向的步骤,编排器甚至会自动将 maxIterations 提升到全局上限,这样合理分页循环不会因为记错的默认值而被误杀。
结果信号是一个刻意精简的协议,只在 maxIterations > 1 时才会被检查:
普通步骤的返回值是纯数据,永远通过 onSuccess 推进——不会因为有人返回了一个看起来有点像"未就绪"的对象就引发意外无限循环。
步骤之间通过 __stepResults__(按步骤 id 索引的先前结果映射)和 __lastResult__(上一步的结果)通信,这些都会被注入到每个步骤的执行上下文中。由于 __lastResult__ 在步骤自身的重试过程中保持不变,列表迭代可以压缩成单个自轮询步骤:"从 __lastResult__ 中打开第 i 项、提取、递增、还没完成"——而不需要在你无法控制的主语言里手写外层循环。
因为控制流是数据,所以可以在保存时检查。每条持久化路径——向导、服务导入、HTTP 步骤 CRUD 端点——都会运行链验证:每个 onSuccess/onFailure 目标必须存在、不能有孤立步骤、不能有重复 id、不能有自循环哨兵(旧的 onSuccess: 'SELF' 约定被移除正是因为其语义是个陷阱)。变更通过重链接辅助函数处理,所以插入或删除步骤会重连链路而不是静默悬空。
这就是"你画的图"和"你调试的图"之间的区别。配置错误的轮询步骤——比如 onSuccess 指向下一步但 maxIterations: 1——只会执行一次然后推进而不重试。这是一个可见的、可调试的失败,而不是静默的错误执行。
步骤图模型不仅仅比脚本更整洁——它是可信赖的 LLM 代码生成的支持底层:
生成代码保持小巧。LLM 写的是叶子片段("等待这个选择器"、"提取这些字段"),而不是控制流。边是结构化数据,向导可以在部署前验证。小巧的生成目标意味着模型产生幻觉时影响范围也小。
自动修复有可替换的单元。当已部署服务失败时,失败被局部化到一个步骤;修复循环把该步骤的脚本 + 错误 + 清理后的 DOM 快照反馈给 LLM,然后交换进重写后的版本。你不需要重建整体然后祈祷——只需打一个节点补丁然后重新运行图。
执行可回放、可检查。编排器每次运行返回每个步骤的结果和页面快照。"重新设计后哪个步骤出现了分歧?"的答案一目了然。
同一套引擎兼作测试自动化。点击、输入、等待、断言、分支——步骤图是一个自我修复、可回放的 Web 测试。该项目明确将自己定位为轻量级自动化工具,而不只是爬虫。
步骤在通过离屏文档访问的沙盒 iframe 中执行(MV3 CSP 在扩展环境中禁止 eval;声明的沙盒页面是合规的入口),而 $ 前缀的 DSL 原语($click、$extract、$wait、$openTab、$extractWithHover、$scrollToBottom——共 19 个)向目标标签页中的内容脚本转发。DOM 操作发生在页面中;代码在隔离区运行;编排器永远不会阻塞你关心的任何页面的 UI 线程。
部署后,整个图通过本地 HTTP API 调用:POST /api/v1/services/{name}/execute → jobId → GET /jobs/{id}/wait。在运行时,没有任何步骤调用 LLM——生成和修复都是构建时或失败时才触发的事件。
如果你的爬虫代码库已经堆积了重试循环、" sleep-and-pray"式的等待、以及每个网站的"工具函数",问题不在于编码规范——而在于执行模型。脚本隐藏了状态机;步骤图让状态机成为一等公民、可验证的、可机器修复的。Scrapewright 是这一论点的可行实现,GPLv3 跨平台,带有 LLM 接入。
克隆它,从 examples/ 导入一个示例,然后看向导和一个真实网站争论一会儿。你在一个小时内就会知道你的下一个爬虫是否应该是一个图。