暗无天日

=============>DarkSun的个人博客

从CSS选择器到自然语言:网页自动化的两种范式与取舍框架

引子:为什么写爬虫这么容易坏

假设你要从某个电商网站抓取笔记本电脑的价格。传统做法是打开浏览器的开发者工具, 找到价格元素的 CSS 类名, 然后这样写:

price = await card.locator("h4.price").text_content()

这条代码今天能跑。但明天呢?设计稿一改, 这个类名很可能就变了。你得重新打开开发者工具, 重新定位, 重写选择器。页面改版一次, 爬虫就死一次。

维护 CSS 选择器的成本常常被低估:初版写爬虫花 1 小时, 后续维护可能花 10 小时。而且每次改动都是重复劳动, 没有任何累积价值。

如果换一种思路呢?不说"提取 h4.price 的内容", 而是说"帮我找到所有 NVIDIA 显卡的游戏本, 提取名字、价格和 GPU 型号"。让程序自动理解页面, 自动定位元素, 而不是你替它定位。

这就引出了网页自动化的两种不同范式。

两种范式

精确指令范式

这是传统的做法:你告诉程序每一步怎么做。

1. 打开 URL
2. 找到 class="product-card" 的元素
3. 在它里面找到 class="price" 的元素
4. 读取文本内容

优点是指令精确, 执行速度快, 结果确定。但缺点也很明显:你对页面的结构做了假设, 页面一改假设就失效。每次页面更新都是一次人工维护。

目标描述范式

这是新做法:你告诉程序你想要什么, 它自己想办法。

目标:找到 RTX 50 系列的游戏本, 预算 1500 美元以内, 提取名称、价格和显卡型号

程序(一个 LLM 驱动的 Agent)自己打开网页, 自己识别哪些元素是产品卡片, 自己找到价格和规格信息。你不需要关心页面的 HTML 结构。

缺点是慢(每次执行都需要 LLM 推理), 贵(每次调用都有 Token 成本), 而且结果不保证一致。同一个任务跑两次可能拿到不同的数据。

核心区别

维度 精确指令 目标描述
你写什么 操作步骤 最终目标
对页面结构的依赖 强, 类名/层级固定 弱, 语义理解
页面改版后 代码需要重写 通常还能跑
执行速度 毫秒到秒级 秒到分钟级
运行成本 几乎为零 每次有 LLM 推理成本
结果确定性 完全相同 每次可能不同
适用任务 规则明确的操作 需要判断和综合的任务

这个表格说明一件事:两个范式没有绝对优劣。重点不在"哪个更好", 得先搞清楚"什么时候用哪个"。

自主化的自动化:Agent + Judge 架构

目标描述范式内部是一套闭环系统, 不是调一次 LLM 就完事。

Worker 循环

Agent 拿到任务后进入一个循环:

  1. 执行 (Action) :从内置工具箱中选一个操作(导航、点击、提取、滚动)
  2. 评估 (Eval) :上一步操作成功了吗?如果失败就重试
  3. 记忆 (Memory) :记录已经完成的工作, 避免重复
  4. 规划 (Next Goal) :基于当前进度决定下一步做什么

这个循环的好处是能自我纠正。传统脚本碰到意外情况就直接崩溃, Agent 的 Eval 步骤能发现失败并调整策略。比如某个按钮没找到, 不会抛出异常退出, 而是尝试其他方式定位。

Judge 独立审查

Worker 完成后, 第二个 LLM 作为 Judge 审查结果。

这个设计的思路是这样的:人做复杂任务时容易自我感觉良好, 看不到自己的盲区。LLM 也一样。Worker 看到的是完整的思考过程, 容易觉得自己"做得还不错"。Judge 只看到原始任务和最终结果, 能以新视角找出问题。

在该示例中, Judge 发现 Worker 把一篇关于加密技术的文章错误归类为 AI 相关文章。这个分类错误并不显眼, 但 Judge 抓住了。

通用设计原则

Agent + Judge 架构不限于网页爬虫。它的本质是两个角色的分离:

  • 一个能做决策的执行器 + 一个能发现问题的评审器
  • 执行器负责灵活应对变化, 评审器负责把住质量底线
  • 两者视角不同, 才能相互制衡

如果你在设计 AI 工作流, 可以试试这个分离模式。

四个决策维度

下面是四个核心取舍维度, 帮助你判断哪种范式适合当前任务。

速度 vs 灵活性

精确指令范式快。定位一个元素是毫秒级的操作。但这个快建立在"页面结构稳定"的假设上。页面一改, 快就没意义了, 因为代码跑不了。

目标描述范式慢。每次操作都有 LLM 推理延迟, 一个简单的数据提取任务可能花 30 到 60 秒。但这个慢换来了适应性:页面结构变了, Agent 通常还能自己找路。

取舍逻辑:如果页面稳定、任务频繁执行, 精确指令更划算。如果任务是一次性的、或者目标站点经常改版, 目标描述的总体成本(开发时间加维护时间)反而更低。

确定性 vs 适应性

精确指令范式每次执行结果完全相同。这在很多场景下是硬要求。比如金融数据的定时采集, 结果不一致比慢更可怕。

目标描述范式每次都可能走不同路径。同一个提取任务, 两次运行之间分类结果可能不一样。

取舍逻辑:需要审计级可靠性的场景选精确指令。能容忍"今天的结果和昨天有细微差异"的场景再考虑目标描述。

成本结构

精确指令范式的成本是一次性的开发成本。写爬虫花时间, 但跑起来几乎不花钱。

目标描述范式正相反。每次执行都有 Token 成本。Hacker News 示例跑了 42,339 个 Token, 花费约 12 美分(用 GPT-4o)。看起来不多, 但请注意:后续步骤会携带完整对话历史, 步骤越长上下文越大, 成本会加速增长。

降低成本有三种方式:

  • 换便宜模型。GPT-4o-mini 或 Haiku 能把成本降到两美分以下
  • 用本地模型。Ollama 或 LM Studio 可以把单次运行成本降到零。注意需要 30B+ 参数级别的模型才能可靠输出结构化结果
  • 缩短提示词。每少一步推理, 上下文就少几千 Token

任务类型

精确指令适合规则匹配型任务。"提取 class=price 的文本内容"、"点击 class=next-page 的按钮"。这些任务不需要 LLM, 用了反而添乱。

目标描述适合判断综合型任务。"找出所有 AI 相关的故事"、"总结这些产品的共同点"、"挑出性价比最高的那一款"。这些任务需要某种程度的理解, 纯规则无法完成。

新范式什么时候翻车

目标描述范式不是万能药。Newegg 购物网站的实验就是一个典型的失败案例。

任务是筛选"1500 美元以内、RTX 50 系列、32GB 内存"的游戏本。Agent 出了三个问题:

  1. 价格过滤器输入了数值但没有点击 APPLY。页面没刷新, 结果里混入了超出预算的产品
  2. 32GB 内存条件被静默忽略。最终返回的全部是 16GB 内存的笔记本
  3. 翻页只做了第一页, 没有收集全部结果

三个问题指向同一个根因:LLM 对精确约束条件不够敏感。Agent 倾向于"差不多就行了", 而不是"必须严格满足每个条件"。

翻译成更通用的表述:目标描述范式适合"软约束"任务(帮我找有趣的故事), 但不适合"硬约束"任务(必须精确满足 N 个条件)。如果把硬约束任务交给目标描述范式, 约束条件很可能被突破。

决策树

综合上面的分析, 下面是一个简单的决策流程:

  1. 任务涉及精确的数字过滤吗(预算 1500 元、32GB 内存)?
    • 是 → 用精确指令范式。把条件写死在代码里, 确保每个都被严格执行
    • 否 → 看第 2 条
  2. 任务需要判断或分类吗("哪篇是 AI 相关文章""这个产品的卖点是什么")?
    • 是 → 目标描述范式很合适
    • 否 → 看第 3 条
  3. 任务频率高、对速度敏感吗?
    • 是 → 精确指令范式更好
    • 否 → 两个都能用, 按开发时间成本选

决策核心只有一句话: 规则清晰、频繁执行的任务用精确指令;需要判断、页面多变的任务用目标描述。

不是二选一

评论区有一个观点值得单独拿出来说。读者 Jason Zions 提了一个思路:让 LLM 帮你生成 CSS 选择器。

具体做法是这样的:

  1. 用目标描述范式跑一次。Agent 在页面上找到你要的数据
  2. 把 Agent 实际用到的定位方式(CSS 选择器、XPath)记录下来
  3. 以后的任务直接用这些选择器跑精确指令, 又快又省钱
  4. 页面结构改了怎么办?再跑一次 Agent, 更新选择器

这个办法把两种范式串联成一个工作流。LLM 做它最擅长的"适应变化"部分, 精确脚本做它最擅长的"重复执行"部分。谁适合干什么就干什么, 不做非此即彼的选择。

相比直接做单选题, 这个组合方案可能更值得在实际项目中尝试。

总结

网页自动化正在经历从"写指令"到"说目标"的转变。但新范式不是在替换旧范式, 而是在旧范式之上增加了一层能力。

精确指令范式适合高频执行、需要确定性、精确约束的任务。 目标描述范式适合需要判断和综合、页面多变、一次性的任务。

两者之间的选择不是技术选型, 而是对任务本质的判断:你的任务更像一条流水线, 还是更像一次探索?

网页自动化 : LLM : 决策框架 : 范式对比