先说结论:网上对“Agent 通杀网站逆向”的各种说法,很多都是营销话术。Agent 智能体真正能解决的是网页信息提取、流程自动化和结构化数据整理,而不是绕过网站的安全机制。本文会把“AI Agent 做网站数据自动化”这件事讲清楚:架构怎么搭、环境怎么准备、接口怎么调、批量任务怎么跑,以及哪些事情绝对不能做。
文章会给出通用可落地的工程方案:LLM 负责规划与语义理解,Playwright 负责浏览器控制,FastAPI 提供接口服务,任务队列负责批量处理。所有操作都以自有站点、授权测试环境或公开合法数据源为前提。如果你关心的是合法范围内的网页数据采集与流程自动化,这篇可以直接收藏。
1. 核心能力速览
在动手之前,先把能力边界列成一张表。这里说的“Agent 智能体”不是一个单一开源项目,而是一套基于 LLM + 浏览器自动化 + 结构化输出的通用流程框架,底层全部使用公开生态里的成熟组件。
| 能力项 | 说明 |
|---|---|
| 项目类型 | 通用 Agent 流程框架,由 LLM 规划器 + 浏览器自动化工具 + 结构化输出层组成 |
| 核心功能 | 网页文本提取、表格结构化、表单填写、分页遍历、登录后页面解析、批量信息收集 |
| 运行依赖 | Python 3.10+、Node.js(可选)、Chromium 浏览器、LLM API 或本地模型服务 |
| 模型接入 | OpenAI 兼容接口,支持调用云端 API,也支持 Ollama / vLLM 等本地模型服务 |
| 推荐硬件 | CPU 即可运行;若使用本地 LLM,显存需按模型实际规格评估 |
| 启动方式 | 命令行单次运行 + FastAPI 启动 Web 服务 |
| API 能力 | 提供 HTTP 接口,可接收任务并返回结构化 JSON |
| 批量任务 | 支持目录批量处理、队列顺序执行、失败重试 |
| 输出格式 | JSON、Markdown、本地文件归档 |
| 适合场景 | 自有系统数据核对、公开数据整理、文档站抓取解析、自动化流程验证 |
| 不适合场景 | 绕过登录态、破解验证码、对抗反爬风控、抓取受限数据 |
这里必须强调一个原则:Agent 只能访问你有权访问的数据。凡是需要绕过网站安全机制才能拿到的内容,都不是这套框架应该解决的问题。
2. 适用场景与使用边界
2.1 合法且高频的落地场景
首先是自有站点或内网系统的数据核对。很多运营人员需要定期从后台导出报表再整理成固定格式,Agent 可以自动登录、读取页面数据、按模板汇总,省掉重复点击。
其次是公开文档站的结构化整理。比如技术文档、产品说明、行业报告页面,Agent 可以抽取正文、表格、关键字段,导出为 Markdown 或 JSON。这里要注意遵守站点本身的使用条款和 robots.txt 限制。
第三是授权测试环境下的流程自动化。在本地搭建演示系统,或者在公司测试环境执行表单提交、按钮点击、分页浏览,Agent 可以反复尝试,替代手工回归测试。
2.2 明确不能做的场景
电商平台是这类话题的高发区。很多电商站点有严格的反爬风控,页面结构也会频繁变化,且用户协议通常禁止未经授权的数据采集。不要尝试用 Agent 对抗验证码、模拟登录态绕过风控,或者高频请求公开商品页。正确的替代路径是:使用平台开放 API,或者通过数据授权合作获取结构化数据。
另一个不能做的是绕过登录限制读取付费内容。这类行为可能违反账号使用规则和相关法律法规,风险远高于收益。
2.3 隐私与版权红线
如果目标页面涉及个人信息(手机号、地址、订单记录),采集前必须确认是否有合法处理依据,并做好脱敏和权限控制。输出结果如果包含第三方版权内容,只能用于个人学习或已获授权的用途,不能直接商用发布。
3. 环境准备与前置条件
整套框架的核心是“LLM 做计划,浏览器执行,代码做校验”。环境准备分成三层:LLM 服务、浏览器自动化环境、项目依赖。
3.1 LLM 服务选择
Agent 的规划能力由 LLM 决定。你有两种接法:
- 云端 API:直接用 OpenAI 兼容接口,优点是无需 GPU,缺点是每次调用产生费用,且数据会发送到第三方服务。敏感页面不适合走这条路。
- 本地模型:用 Ollama 或 vLLM 起一个本地 API 服务,数据不出本机,但需要按模型规格准备内存和显存。7B 级别模型在小规模任务上表现尚可,复杂长页面需要更强模型。
本地模型启动示例(Ollama):
# 安装 ollama 后拉取模型,这里以 qwen2.5 为例 ollama pull qwen2.5:7b ollama serve启动后本地 API 地址为http://127.0.0.1:11434/v1,兼容 OpenAI 的/v1/chat/completions接口。
3.2 检查清单
| 检查项 | 要求说明 |
|---|---|
| 操作系统 | Windows / Linux / macOS 均可 |
| Python | 3.10 及以上 |
| Chromium | 通过 Playwright 安装,用于页面渲染 |
| 磁盘空间 | 浏览器约 300MB,虚拟环境 2GB 左右 |
| 内存 | 本机跑 7B 模型建议 16GB 以上 |
| 显存 | 本机跑 7B 模型建议至少 8GB,具体以模型为准 |
| 端口 | 11434(本地模型)、8000(Agent API 服务) |
| 网络 | 能访问目标测试站点和模型服务 |
启动前先确认端口没有被占用:
# Linux / macOS lsof -i :8000 lsof -i :11434 # Windows PowerShell netstat -ano | findstr :8000如果端口被占用,要么关掉旧进程,要么换端口启动。
4. 智能体整体架构与关键设计
4.1 三层架构
整个 Agent 框架拆成三层:
- 规划层:接收用户任务,调用 LLM 将任务拆成可执行步骤,例如“打开页面 -> 定位正文 -> 抽取标题和表格 -> 输出 JSON”。
- 执行层:通过 Playwright 控制浏览器执行规划层给出的动作,包括页面访问、点击、输入、滚动、翻页。
- 校验层:执行结束后检查结果完整性,如果页面结构变化导致抽取失败,则把错误信息回传给规划层,重新生成修正步骤。
这样的结构与单独写死爬虫脚本的区别在于:脚本依赖固定的 CSS 选择器,而 Agent 可以用语义描述定位页面元素,例如“找到商品列表区域”“把表格第一列作为 key”,不绑定具体页面结构。
4.2 Agent 工作循环
核心循环如下:
- 用户提交任务描述和目标 URL。
- 规划层生成操作步骤清单。
- 执行层逐条执行浏览器动作。
- 校验层判断当前步骤是否成功。
- 若失败,将页面快照和错误文本回传给 LLM,重新规划。
- 全部步骤完成后,统一输出结构化结果。
这个循环相当于一个简化版 ReAct 模式:Thought -> Action -> Observation,直到完成用户目标。
4.3 工具函数设计
执行层建议把浏览器能力封装成独立工具函数,便于 Agent 调用,也方便人工单测。常见工具函数如下:
| 工具名 | 入参 | 输出 | 说明 |
|---|---|---|---|
| open_page | url | 页面摘要 | 打开页面并截取可见文本 |
| extract_text | selector | 文本内容 | 按语义描述或选择器提取文本 |
| extract_table | selector | JSON 数组 | 将页面表格转换为结构化数组 |
| fill_form | selector, value | 成功/失败 | 填写表单字段 |
| click_button | selector | 成功/失败 | 点击按钮 |
| paginate | direction | 成功/失败 | 翻页操作 |
| snapshot | 无 | 截图路径 | 保存当前页面截图,用于失败回溯 |
工具封装得越干净,Agent 的规划成功率越高。不要把页面操作散落在业务流程里。
5. 搭建部署与启动方式
5.1 安装项目依赖
先创建虚拟环境并安装 Python 依赖:
python -m venv venv source venv/bin/activate # Windows 使用 venv\Scripts\activate pip install playwright fastapi uvicorn openai pydantic安装 Playwright 浏览器内核:
playwright install chromium如果系统缺少浏览器运行库,Linux 环境还要安装依赖:
# Ubuntu / Debian 示例 playwright install-deps这里不需要 GPU,CPU 环境可以正常完成页面渲染和文本提取。
5.2 项目目录结构
建议按以下结构组织:
agent-project/ ├── agent/ │ ├── planner.py # LLM 规划器 │ ├── executor.py # 浏览器执行器 │ ├── validator.py # 结果校验器 │ └── tools.py # 工具函数集 ├── app.py # FastAPI 入口 ├── tasks.json # 批量任务配置 ├── inputs/ # 输入素材 ├── outputs/ # 输出结果 └── logs/ # 运行日志5.3 核心代码:Agent 主循环
下面给出一段可以跑通的简化版 Agent 主循环,实际项目可以在此之上扩展:
# agent/core.py from playwright.sync_api import sync_playwright from openai import OpenAI class WebAgent: def __init__(self, base_url: str, api_key: str, model: str = "qwen2.5:7b"): self.client = OpenAI(base_url=base_url, api_key=api_key) self.model = model self.tool_names = ["open_page", "extract_text", "extract_table", "fill_form", "click_button", "paginate"] def plan(self, task: str, page_info: str) -> list[dict]: prompt = f""" 当前可调用工具:{self.tool_names} 目标任务:{task} 当前页面信息:{page_info} 请返回 JSON 格式的操作步骤列表,字段为 step, tool, params。 """ resp = self.client.chat.completions.create( model=self.model, messages=[{"role": "user", "content": prompt}], temperature=0.2 ) return eval(resp.choices[0].message.content) # 仅作示例,生产环境建议用 json.loads 解析 def run(self, task: str, start_url: str): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto(start_url, wait_until="domcontentloaded") steps = self.plan(task, page.inner_text("body")[:2000]) results = [] for step in steps: try: result = self._execute_tool(page, step["tool"], step["params"]) results.append({"step": step["step"], "status": "ok", "result": result}) except Exception as exc: results.append({"step": step["step"], "status": "error", "error": str(exc)}) break browser.close() return results def _execute_tool(self, page, tool: str, params: dict): # 实际项目在这里把工具名映射到 tools.py 里的函数 if tool == "open_page": page.goto(params["url"], wait_until="domcontentloaded") return page.title() if tool == "extract_text": return page.inner_text(params["selector"]) raise NotImplementedError(f"工具 {tool} 未实现")需要注意:示例中的eval仅用于演示可读性,生产环境必须使用json.loads并要求 LLM 严格输出 JSON,避免表达式注入风险。
5.4 启动 Web API 服务
用 FastAPI 把 Agent 包成 HTTP 服务:
# app.py from fastapi import FastAPI from pydantic import BaseModel from agent.core import WebAgent app = FastAPI() agent = WebAgent( base_url="http://127.0.0.1:11434/v1", api_key="local", model="qwen2.5:7b" ) class TaskRequest(BaseModel): task: str start_url: str @app.post("/run") def run_task(req: TaskRequest): return agent.run(req.task, req.start_url)启动服务:
uvicorn app:app --host 0.0.0.0 --port 8000启动后可以通过http://127.0.0.1:8000/docs查看 Swagger 文档,也可以直接用 POST 请求调用接口。如果端口冲突,换一个端口即可。
6. 功能测试与效果验证
框架搭好后,建议按以下顺序跑功能测试。为保证合规,所有测试目标都使用本地演示页面或公开合法数据源。
6.1 静态信息提取测试
测试目的:验证 Agent 是否能把网页正文和标题抽取为结构化 JSON。
操作步骤:使用一个公开技术文档页面或本地静态 HTML 文件,提交任务“提取页面标题、第一段正文和所有二级标题”。
请求示例:
curl -X POST http://127.0.0.1:8000/run \ -H "Content-Type: application/json" \ -d '{"task": "提取页面标题、第一段正文和所有二级标题", "start_url": "http://127.0.0.1:8080/test.html"}'预期结果:返回 JSON 中包含完整字段,标题正确、正文无乱码、二级标题数量与实际页面一致。
判断标准:字段级校验通过,缺失字段为 0。
失败排查:如果提取结果为空,先检查目标页面是否被正常加载,再确认页面正文是否通过 JavaScript 动态渲染。
6.2 表格转结构化数据测试
测试目的:验证表格抽取能力,这是 Agent 在数据处理场景中最常被用到的功能。
操作步骤:准备一个包含多行多列的本地 HTML 表格页面,提交任务“把页面上第一个表格转成 JSON”。
预期结果:表格每一行变成一个 JSON 对象,表头成为字段名,空单元格保持为空字符串而不是丢弃。
常见问题:多层表头、合并单元格会显著增加解析难度。如果表格结构复杂,应该让 Agent 先输出 Markdown 格式,再二次转换。
6.3 表单填写与点击流程测试
测试目的:验证 Agent 能否根据语义描述完成“输入关键词 -> 点击搜索 -> 读取结果”的整段流程。
操作步骤:在本地测试系统准备一个带搜索框的页面,提交任务“在搜索框输入 Agent,点击搜索按钮,读取结果区域文本”。
预期结果:搜索框正确填入内容,按钮点击成功,返回结果区域包含目标关键词。
判断标准:观察 Playwright 日志,确认每个动作都有明确成功标记;如果点击后页面跳转,需要等待新页面加载完成再执行下一步。
失败排查:如果定位失败,将页面截图回传给 LLM,用图片描述替代纯文本描述,成功率会明显提升。
6.4 分页与动态加载测试
测试目的:验证翻页和滚动加载。
操作步骤:在本地测试页模拟分页列表,提交任务“遍历前 3 页,收集所有列表项标题”。
预期结果:返回结果包含 3 页的数据总和,且没有重复项。
注意点:分页遍历必须加入页数上限,否则动态加载页面可能无限制滚动下去。批量任务中建议统一加 max_pages 参数。
6.5 输出质量校验
每次任务结束后都应该跑一遍字段完整性检查。可以写一个独立校验函数:
def validate_result(data: dict, required_keys: list[str]) -> bool: missing = [key for key in required_keys if not data.get(key)] if missing: print(f"缺少字段: {missing}") return False return True校验不通过时,把缺失字段名和页面摘要重新交给 LLM 做第二次规划,而不是直接丢弃结果。这比提高 prompt 温度参数更有效。
7. 接口 API 与批量任务
7.1 API 设计原则
接口层需要区分两类能力:单次任务接口和批量任务接口。单次任务用于联调和测试,批量任务用于生产环境。批量任务不建议同步等待,而应采用“提交任务 -> 后台执行 -> 轮询状态”的异步模式。
扩展 FastAPI 接口:
# app.py 追加 import uuid from pydantic import BaseModel BATCH_TASKS = {} class BatchRequest(BaseModel): tasks: list[dict] @app.post("/batch") def create_batch(req: BatchRequest): batch_id = str(uuid.uuid4()) BATCH_TASKS[batch_id] = {"status": "running", "results": [], "total": len(req.tasks)} # 实际项目这里应丢给后台任务队列处理,而不是直接阻塞 return {"batch_id": batch_id, "status": "accepted"} @app.get("/batch/{batch_id}") def get_batch(batch_id: str): return BATCH_TASKS.get(batch_id, {"status": "not_found"})7.2 Python 客户端调用示例
import requests import time BASE_URL = "http://127.0.0.1:8000" def submit_batch(tasks): resp = requests.post(f"{BASE_URL}/batch", json={"tasks": tasks}, timeout=30) batch_id = resp.json()["batch_id"] print("批量任务ID:", batch_id) return batch_id def wait_batch(batch_id, interval=5, timeout=300): for _ in range(timeout // interval): status = requests.get(f"{BASE_URL}/batch/{batch_id}", timeout=30).json() if status["status"] != "running": return status time.sleep(interval) return {"status": "timeout"}7.3 批量任务配置模板
批量任务建议使用 JSON 文件维护,方便审计和复用:
{ "max_retry": 3, "rate_limit_seconds": 2, "tasks": [ { "id": "task-001", "task": "提取页面标题和第一段", "start_url": "http://127.0.0.1:8080/doc1.html", "required_keys": ["title", "summary"] }, { "id": "task-002", "task": "提取表格为JSON", "start_url": "http://127.0.0.1:8080/doc2.html", "required_keys": ["table"] } ] }rate_limit_seconds字段用来控制相邻请求间隔,这对任何网站都是必要的。不要删除这个字段,也不要把间隔设为 0。
失败重试策略:单个任务失败时先记录错误快照,间隔指数退避重试(如 3 秒、9 秒、27 秒),超过最大重试次数后将该任务标记为 failed,并继续执行后续任务。不要让一个失败任务卡死整个批次。
8. 资源占用与性能观察
8.1 LLM Token 开销
Agent 每轮规划都需要把任务描述、页面文本、当前状态发送给 LLM。一个普通页面正文约几千字,换算成 token 大约是 1000 到 3000。如果循环中每步都携带完整页面文本,token 消耗会快速增长。
优化方式:
- 截断页面文本:默认只取 body 前 2000 到 4000 字符。
- 缓存页面摘要:同一 URL 在短时间内的页面摘要直接复用。
- 使用小模型做规划:规划任务是简单 JSON 输出,7B 级别模型足够,不必每次都调用大模型。
8.2 浏览器内存与并发
从常见部署形态看,单个 Chromium context 的内存开销在数百 MB 量级。如果同时开多个并发任务,内存压力会成倍上升。建议控制 Chrome 实例数量,优先用同一个浏览器实例内开多个 tab,而不是多个独立浏览器进程。
使用headless=True能省掉渲染开销,常规文本提取任务不需要窗口可见。只有在需要截图排障时才临时开启有头模式。
8.3 性能观察指标
建议记录以下指标:
- 单任务总耗时
- LLM 调用次数
- 单次调用的 token 数
- 浏览器内存峰值
- 失败重试次数
- 某个固定步骤的成功率
通过观察这些指标,可以判断瓶颈在 LLM 规划、页面渲染还是目标结构变化。页面结构变化是 Agent 方案最值得关注的风险点:目标站点改版后,旧规则全部失效。缓解策略是让 Agent 输出更依赖语义描述而不是固定选择器,并在每次任务前先做一次页面结构摘要。
9. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 依赖安装失败 | Python 版本过低或网络源不可用 | 查看 pip 日志 | 升级 Python,换国内镜像源 |
| Chromium 启动失败 | 缺少系统运行库或浏览器内核未安装 | 运行 playwright install-deps | 安装系统依赖后重启 |
| LLM 接口超时 | 模型服务未启动或网络不通 | curl 测试接口 | 确认 ollama serve 正常 |
| Agent 返回空结果 | 页面为动态渲染,正文未加载完成 | 截图检查页面状态 | 增加 wait 策略 |
| 数据提取缺字段 | 页面结构与 prompt 描述不符 | 打印页面摘要 | 用语义描述替换选择器 |
| 批量任务卡住 | 单任务异常未捕获 | 查看任务日志 | 加超时控制,失败跳转 |
| 被目标站点限制访问 | 请求频率过高 | 查看 HTTP 状态码 | 拉大间隔,改用官方 API |
| 端口冲突 | 旧进程残留 | netstat 检查端口 | kill 旧进程或换端口 |
额外提醒:如果目标站点返回验证码或风控拦截,正确的做法是立即停止采集并调整方案,而不是尝试绕过。一旦触发 IP 限制,解决成本远高于采集收益。
10. 合规与工程化最佳实践
10.1 授权边界检查清单
每次新增目标站点前,先做五步检查:
- 确认自己是否有合法访问权限,例如自有站点、团队内部系统或已获得授权。
- 阅读目标站点的 robots.txt 和使用条款,确认是否允许自动化访问。
- 评估数据性质:是否涉及个人信息、付费内容或商业机密。
- 控制访问频率,避免对目标站点造成负载压力。
- 保留任务日志,记录采集时间、目标地址和用途。
# 检查目标站点的抓取约定 curl https://example.com/robots.txt10.2 数据与日志管理
输出结果按来源和用途分目录存放,敏感字段在落盘前做脱敏。任务日志保留至少 30 天。Agent 的每个决策步骤都尽量记录:使用哪个模型、输入了什么任务、调用了什么工具、返回了什么结果。这样一旦出现数据问题,可以回溯到具体环节。
10.3 模型与接口安全
如果 Agent 服务对外提供接口,必须限制访问范围。生产环境不要直接暴露到公网,可以绑定内网 IP,或增加 Token 鉴权:
uvicorn app:app --host 127.0.0.1 --port 8000对外提供服务时,把 API Key 放在环境变量中,不要写进代码仓库:
export LLM_BASE_URL=http://127.0.0.1:11434/v1 export LLM_API_KEY=your_key10.4 上线前效果复核
批量采集结果上线前,人工抽样核对 5% 到 10% 的数据。Agent 的抽取质量受页面结构影响较大,特别是表格和复杂排版。宁可保留一份经过人工复核的小数据集,也不要直接发布未校验的全量结果。
11. 总结与下一步
AI Agent 做网站信息提取的完整链路是:LLM 负责语义理解,浏览器自动化负责操作,代码校验负责兜底。这套架构能够覆盖公开文档整理、自有系统数据核对、表单流程自动化等场景,门槛不高,CPU 机器可以跑,部署核心就是 Python 环境加浏览器。第一批建议做的测试很简单:找一个本地 HTML 页面,让 Agent 把标题和表格提取成 JSON,跑通后再加上批量任务和失败重试。
最容易踩的坑有三个:第一,没有控制请求频率被目标站点限制;第二,用固定选择器依赖页面结构,页面一改就失效;第三,批量任务没有超时和重试机制,单任务失败卡死整个队列。这些坑都能通过日志和校验流程提前发现。
更需要注意的是边界问题。Agent 能自动化流程,但不能自动化“授权”。目标站点允不允许自动化访问、数据能不能采集、用在哪,这些判断不能交给模型决定,只能由使用者负责。先在授权环境下验证能力,再逐步放大任务规模,这套框架会非常顺手。下一步可以尝试的方向是:多 Agent 协作做拆分任务、把页面截图纳入模型输入以提升定位准确率、以及将抽取结果接入数据看板做定期核对。