从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 拿到任务后进入一个循环:
- 执行 (Action) :从内置工具箱中选一个操作(导航、点击、提取、滚动)
- 评估 (Eval) :上一步操作成功了吗?如果失败就重试
- 记忆 (Memory) :记录已经完成的工作, 避免重复
- 规划 (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 出了三个问题:
- 价格过滤器输入了数值但没有点击 APPLY。页面没刷新, 结果里混入了超出预算的产品
- 32GB 内存条件被静默忽略。最终返回的全部是 16GB 内存的笔记本
- 翻页只做了第一页, 没有收集全部结果
三个问题指向同一个根因:LLM 对精确约束条件不够敏感。Agent 倾向于"差不多就行了", 而不是"必须严格满足每个条件"。
翻译成更通用的表述:目标描述范式适合"软约束"任务(帮我找有趣的故事), 但不适合"硬约束"任务(必须精确满足 N 个条件)。如果把硬约束任务交给目标描述范式, 约束条件很可能被突破。
决策树
综合上面的分析, 下面是一个简单的决策流程:
- 任务涉及精确的数字过滤吗(预算 1500 元、32GB 内存)?
- 是 → 用精确指令范式。把条件写死在代码里, 确保每个都被严格执行
- 否 → 看第 2 条
- 任务需要判断或分类吗("哪篇是 AI 相关文章""这个产品的卖点是什么")?
- 是 → 目标描述范式很合适
- 否 → 看第 3 条
- 任务频率高、对速度敏感吗?
- 是 → 精确指令范式更好
- 否 → 两个都能用, 按开发时间成本选
决策核心只有一句话: 规则清晰、频繁执行的任务用精确指令;需要判断、页面多变的任务用目标描述。
不是二选一
评论区有一个观点值得单独拿出来说。读者 Jason Zions 提了一个思路:让 LLM 帮你生成 CSS 选择器。
具体做法是这样的:
- 用目标描述范式跑一次。Agent 在页面上找到你要的数据
- 把 Agent 实际用到的定位方式(CSS 选择器、XPath)记录下来
- 以后的任务直接用这些选择器跑精确指令, 又快又省钱
- 页面结构改了怎么办?再跑一次 Agent, 更新选择器
这个办法把两种范式串联成一个工作流。LLM 做它最擅长的"适应变化"部分, 精确脚本做它最擅长的"重复执行"部分。谁适合干什么就干什么, 不做非此即彼的选择。
相比直接做单选题, 这个组合方案可能更值得在实际项目中尝试。
总结
网页自动化正在经历从"写指令"到"说目标"的转变。但新范式不是在替换旧范式, 而是在旧范式之上增加了一层能力。
精确指令范式适合高频执行、需要确定性、精确约束的任务。 目标描述范式适合需要判断和综合、页面多变、一次性的任务。
两者之间的选择不是技术选型, 而是对任务本质的判断:你的任务更像一条流水线, 还是更像一次探索?