
Ethan Collins
Pattern Recognition Specialist

AI 智能体可能从错误的页面接收到看似有效的记录。登录界面可能包含标题,不完整的文档可能成功解析,模型可能在请求的事实缺失时返回JSON。AI 智能体网页抓取架构需要在到达源和解释其内容方面进行独立决策。
本教程围绕不可变快照和版本化记录接口约定设计该边界。该工作流适用于授权收集公共通知、文档更新和其他允许的网页信息。CapSolver 在访问层中作为受支持的CAPTCHA处理功能出现。可运行的示例随后展示了如何分类采集结果、提取小记录集,并在验证失败时保留证据。
访问层应返回可用快照或显式失败;提取层应返回与该快照相关的候选记录。即使浏览器运行时、解析器或模型发生变化,也应保持两个接口约定的稳定性。
该流程为:批准的采集请求 → 访问适配器 → 保留的快照 → 提取适配器 → 验证 → 接受的记录存储 → 智能体。在此设计中,可以再次从存储的证据中运行提取,而无需重新打开浏览器。
为访问适配器提供批准的源、任务标识、时间预算和允许的会话上下文。其工作包括选择所需表示形式、等待相关内容、保留会话所有权并分类失败。网络配置和JavaScript渲染属于您的HTTP或浏览器基础设施。
输出应记录请求和最终源位置、观察时间、表示类型、内容摘要和就绪证据。避免隐藏实际加载页面的单个“成功”标志。最终URL和HTTP状态有帮助,但预期文档标识和所需内容区域也需要检查。
为提取器提供快照引用、模式版本和字段定义。它不应静默导航、更改凭据或选择其他网络路径。返回缺失或模糊字段,而不是要求访问层继续尝试直到某些值出现。
AI网页抓取术语表 描述了AI在收集和解释网页信息中的更广泛应用。此处的边界还支持确定性提取:稳定属性或文档化结构化数据可能就足够。在需要解释时使用模型,同时保留相同的下游验证接口约定。
可用快照必须包含所需字段的证据,并以提取器理解的表示形式存在。根据该要求选择HTML、渲染的DOM或截图。
当响应已包含相关内容时,原始HTML是合适的。如果必要字段仅在客户端执行后出现,浏览器适配器应在任务特定的就绪检查后捕获渲染的DOM。将就绪定义为可观察条件,如预期的记录容器和完成标记,而不是通用的固定睡眠。
记录捕获的表示形式。在渲染标记上测试的解析器不应接收初始HTML外壳,除非明确接口约定变更。如果所需区域缺失,请在尝试提取前将快照分类为不完整。
截图在特定时间提供特定视口的像素。对于基于截图的提取,保留图像尺寸、捕获上下文和每个提取字段的区域引用。如果值位于捕获视图之外,返回为缺失;模型对类似布局的熟悉度不是该值的证据。
在未记录不确定性的情况下,不要将视觉估计转换为精确数字。当DOM和视觉证据都可用时,使用分歧作为审查案例。下面的示例仅实现HTML适配器;视觉适配器需要自己的证据检查和评估集。
重试决策应明确失败层、另一次尝试的预期好处和剩余预算。保持采集和解释重试分离,以防止提取错误产生不受控制的流量。
| 观察 | 所属层 | 推荐操作 |
|---|---|---|
| 读取超时或选定的临时服务错误 | 访问 | 仅在时间预算和尝试预算内重试允许的读取 |
| HTTP 429或服务请求的冷却 | 访问 | 延迟到共享调度器并保留冷却信号 |
| HTTP 401/403或不明确的授权 | 访问 | 停止并审查允许的访问路径 |
| 识别的CAPTCHA挑战 | 访问 | 暂停以进行资格和受支持任务审查 |
| 空响应、错误文档或缺失的必需区域 | 访问 | 保留诊断证据并调查就绪情况 |
| 缺失字段、无效日期、重复记录或模式不匹配 | 提取/验证 | 将候选记录隔离并针对快照重放 |
| 有效形状但不支持的含义 | 验证 | 拒绝或请求审查;不要将流畅文本视为证据 |
在实现第一行时,HTTP语义很重要。 RFC 9110的重试和幂等规则 区分可以安全重复的操作和其效果可能不确定的操作。不要将读取重试循环用于表单提交或其他状态更改操作。
Retry-After标头 可以表达延迟或HTTP日期。保留该值以用于调度。本地工作者不应将服务器请求的等待替换为较短的退避,共享相同允许收集范围的工作者应共享冷却状态。
CapSolver应在您的工作流建立权限、任务兼容性和所需会话上下文后,仅处理记录的CAPTCHA任务。403响应、空页面和CAPTCHA小部件是不同的观察结果;避免将它们全部映射到解决请求。
CapSolver的createTask接口约定 需要适当的任务对象。对于异步工作,getTaskResult返回任务状态和输出。访问适配器仍负责应用记录的集成并在之后检查目标。
完成的任务不是验证的页面快照。在将内容交给提取之前,重新检查文档标识和就绪条件。设置单独的挑战预算,并在挑战不受支持、授权不明确或预期页面仍不可用时停止。AI 智能体浏览器基础设施堆栈 提供了关于运行时所有权和会话证据的相关指导。
领取您的CapSolver优惠码
立即提升您的自动化预算!
在充值CapSolver账户时使用优惠码 CAP26,每次充值可获得额外 5% 的奖励 —— 无限制。
现在在您的 CapSolver仪表板 中领取
以下Python工作流对合成访问回复进行分类,保留接受的HTML,提取公告字段并返回结构化JSON。将其保存为 pipeline_example.py 并使用Python 3.9或更高版本运行;它仅使用标准库。
fetch 函数是注入的只读适配器。在此它提供内存中的固定装置,演示禁用睡眠。未联系任何网站、浏览器服务、模型或CAPTCHA API。challenge 和 ready 字段表示由访问适配器提供的观察结果;示例未实现通用挑战检测器。
解析器使用Python的 HTMLParser回调 来实现一个故意小型的标记接口约定:每个 article 包含一个 h2、记录ID和发布日期。它不是通用的DOM解析器或任意损坏HTML的验证器。
from dataclasses import dataclass
from datetime import date
from hashlib import sha256
from html.parser import HTMLParser
import json
import time
class PipelineError(Exception):
def __init__(self, stage, reason, retry_after=""):
self.stage, self.reason = stage, reason
self.retry_after = retry_after
super().__init__(f"{stage}:{reason}")
@dataclass(frozen=True)
class Reply:
status: int
body: str = ""
content_type: str = "text/html"
challenge: bool = False
ready: bool = True
retry_after: str = ""
def access(fetch, wait=time.sleep):
# fetch is a read-only adapter; all values below are application policy.
for attempt in range(2):
try:
reply = fetch()
except TimeoutError:
if attempt == 0:
wait(0.5)
continue
raise PipelineError("access", "timeout_exhausted")
if reply.status == 429:
# Pass Retry-After to a shared scheduler; do not retry here.
raise PipelineError("access", "defer_rate_limit", reply.retry_after)
if reply.status in (401, 403):
raise PipelineError("access", "authorization_review")
if reply.challenge:
raise PipelineError("access", "challenge_review")
if reply.status == 503 and reply.retry_after:
raise PipelineError("access", "defer_service", reply.retry_after)
if reply.status in (502, 503, 504) and attempt == 0:
wait(0.5)
continue
if reply.status != 200:
raise PipelineError("access", "http_status")
if reply.content_type.split(";")[0].strip().lower() != "text/html":
raise PipelineError("access", "representation_mismatch")
if not reply.ready or not reply.body.strip():
raise PipelineError("access", "incomplete_snapshot")
return reply.body
raise PipelineError("access", "attempts_exhausted")
class BulletinParser(HTMLParser):
# This small parser supports only the documented fixture markup.
def __init__(self):
super().__init__(convert_charrefs=True)
self.rows, self.current, self.in_title = [], None, False
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
if tag == "article":
if self.current is not None:
raise PipelineError("extraction", "nested_record")
self.current = {"id": attrs.get("data-id", ""),
"published": attrs.get("data-published", ""),
"title_parts": [], "title_count": 0}
elif tag == "h2" and self.current is not None:
self.current["title_count"] += 1
self.in_title = True
def handle_data(self, data):
if self.current is not None and self.in_title:
self.current["title_parts"].append(data)
def handle_endtag(self, tag):
if tag == "h2":
self.in_title = False
if tag == "article" and self.current is not None:
self.rows.append(self.current)
self.current, self.in_title = None, False
def extract(html):
parser = BulletinParser()
parser.feed(html)
parser.close()
if parser.current is not None or not parser.rows:
raise PipelineError("extraction", "record_structure")
records, seen = [], set()
for row in parser.rows:
title = " ".join("".join(row["title_parts"]).split())
if not row["id"].strip() or not title or row["title_count"] != 1:
raise PipelineError("extraction", "required_field")
try:
published = date.fromisoformat(row["published"]).isoformat()
except ValueError:
raise PipelineError("extraction", "invalid_date")
if row["id"] in seen:
raise PipelineError("extraction", "duplicate_id")
seen.add(row["id"])
records.append({"id": row["id"], "title": title,
"published": published})
return records
def run(fetch, archive, wait=time.sleep):
html = access(fetch, wait)
digest = sha256(html.encode("utf-8")).hexdigest()
archive[digest] = html # In-memory evidence retained even if parsing fails.
records = extract(html)
return {"schema_version": "bulletins.v1", "source_id": "fixture:bulletins",
"snapshot_sha256": digest,
"records": records}
if __name__ == "__main__":
html = ('<article data-id="notice-1" data-published="2026-09-10">'
'<h2>Maintenance window announced</h2></article>')
replies = iter([Reply(503), Reply(200, html)])
archive = {}
output = run(lambda: next(replies), archive, wait=lambda seconds: None)
print(json.dumps(output, indent=2))
该演示接收合成的503响应,然后是有效的HTML响应。它生成以下结果:
{
"schema_version": "bulletins.v1",
"source_id": "fixture:bulletins",
"snapshot_sha256": "6ed8df5a98ee53e2889feb5ef7ed4dd8d549dba82882580418d4ca9656b7d46b",
"records": [
{
"id": "notice-1",
"title": "Maintenance window announced",
"published": "2026-09-10"
}
]
}
每条记录必须包含ID、一个非空标题和可解析的日期。重复的ID会拒绝该批次。快照摘要将输出与保留的HTML关联,并且提取错误会将该HTML保留在调用者拥有的存档中以供重放。
两次重试尝试和半秒延迟是示例应用程序策略,而非供应商建议。循环会立即推迟429错误,保留503错误的冷却期,并在授权或挑战审核失败时停止。extract中的失败无法再次调用fetch。
实现源准入、重定向检查、支持的内容解码、每请求超时和连接示例到真实传输前的整体截止时间。同步的fetch如果永不返回,则不受尝试计数器限制。将剩余截止时间传递给传输,并在该预算中包含重试等待时间。
用受控存储替换内存中的存档,并在其元数据中添加观察时间戳、实际源身份、提取器版本和模式版本。在解码或解析前应用大小限制。失败或不完整的快照可能仍是有用的诊断证据,但应与可提取的快照池分开保存。
接受的数据除了JSON结构外,还需要检查其含义和覆盖范围。示例验证其小型确定性契约;通用提取服务需要更丰富的接受策略。
从字段定义开始。发布日期、更新日期和集合时间戳描述不同的事件。指定智能体需要哪一个并拒绝替代方案。对于模型生成的字段,附加源跨度或视觉区域,并检查证据是否支持字段的含义。页面上某个匹配词对于价格、可用性或日期等字段来说过于薄弱。
在集合级别检查完整性。空数组可能意味着“无记录”、布局更改、分页不完整或提取失败。仅当源提供明确且验证过的空状态时才接受空结果。该示例拒绝空列表,因为它没有此类契约。
为任务定义时效性窗口。重新提取旧快照可能修复解析器问题,但不会使底层观察结果变得最新。在重放键中包含快照身份和提取器/模式版本,然后通过幂等存储操作发布接受的记录。将被拒绝的候选记录保留用于有限的诊断审查,而不是将其混入智能体的工作数据集。
在整个过程中将页面内容视为不可信输入。OWASP的提示注入指南描述了嵌入在外部内容中的指令带来的风险。将提取工具与凭据和关键操作隔离;源文本不得获得更改收集范围或发送数据的权限。
边界测试应验证返回结果以及不存在意外的额外工作。通过解析器测试本身并不能证明访问层能正确停止。
对于此示例,测试超时后成功、重复的临时错误、标记为挑战的200响应、429错误及冷却期、不支持的表示形式、缺失标题、无效日期和重复ID。计算适配器调用次数:挑战案例应在一次调用后停止,解析失败应保留快照且不再进行其他访问尝试。
附带的本地测试套件通过了21个测试用例,包括重试耗尽、冷却期保留、快照保留和确定性重放。这些是合成软件检查,而非实时源成功率或模型提取基准。
部署前,添加一个小的允许阶段源并测试实际渲染准备情况、重定向处理、会话过期、传输取消和输出证据。分别测量合格快照和接受记录。这种区分可以告诉你当最终接受率变化时是改进采集还是解释。
当每个阶段都有可观察的输出和明确的负责人时,AI 智能体网页抓取架构更容易操作。保持访问接口约定专注于合格快照,提取接口约定专注于候选字段,验证专注于证据、完整性和时效性。
从本地工作流开始,测试负面路径,并在适配器的限制明确后仅连接允许的源。对于需要记录验证码处理的工作流,在该访问边界内评估CapSolver,并在提取恢复前验证目标。
Q: 网页访问层和数据提取层有什么区别?
访问层获取合格页面快照并分类采集失败。提取层将该快照解释为候选字段。单独的接受检查决定结果记录是否适合智能体。
Q: AI模型应接收HTML、DOM快照还是截图?
使用包含所需字段证据的表示形式。HTML适合服务器提供的内容,渲染的DOM可捕获客户端内容,截图可支持带区域引用和不确定性检查的视觉解释。
Q: 缺失字段是否应触发另一页请求?
缺失字段应首先触发保留快照的审查或重新提取。仅当证据显示快照不完整或过时且访问策略允许另一次尝试时,才请求新页面。
Q: CapSolver在此架构中处于什么位置?
CapSolver位于访问层中受支持的验证码任务接口后,用于授权工作流。您的应用程序拥有任务资格、会话上下文、重试预算,并在任务完成后验证目标。
Q: Python示例是否执行实时AI网页抓取?
不。示例在本地执行基于 HTML 测试样例的流水线并测试其契约。实时部署必须添加允许的访问适配器;基于模型或视觉的提取还需要其自身的实现和基于证据的评估。