MCP 网页抓取工具:内容提取、结构化输出与合规边界

MCP 网页抓取工具实践:抓取流程设计、HTML 内容提取、结构化 JSON 输出、robots 合规与反爬边界、会话管理、JS 渲染页面处理,帮助 LLM 安全合规地获取网页信息。

1. 网页抓取工具的定位

模型的知识有截止时间,网页是实时信息的来源:最新文档、产品价格、竞品动态、新闻事件。网页抓取 MCP 工具让模型「去网上查」——这是搜索工具之外的另一种获取实时信息的方式,尤其适合「打开具体某个页面看内容」的场景。

1.1 与搜索的区别

# 搜索工具: 给 query,返回候选链接与摘要
# 抓取工具: 给 URL,返回页面内容
# 组合使用
# 1) 模型先搜索找到候选 URL
# 2) 再调抓取工具打开具体页面
# 3) 或直接给已知 URL(用户提供的链接)
# 抓取是"点开链接看正文",搜索是"找链接"

1.2 抓取工具的复杂度

层级内容难度
静态抓取纯 HTML 页面、读取低
内容提取去掉导航/广告,取正文中
结构化输出按 schema 抽取字段中
JS 渲染需要执行 JavaScript高
会话/登录需要 Cookie/Token高
反爬对抗验证码/风控极高(应避免)

2. 抓取流程设计

一个抓取工具不是「拿个 HTML 就完事」,而是一条流水线:取回 → 清理 → 提取 → 结构化 → 返回。

2.1 流水线架构

# 抓取流水线
# 1) fetch: 下载页面(HTTP 客户端)
# 2) parse: 解析 HTML(DOM 树)
# 3) clean: 移除脚本/样式/导航/广告
# 4) extract: 提取正文(readability 算法)
# 5) structure: 按 schema 抽取字段(可选)
# 6) return: 按 token 预算返回
# 每一步独立可测,出问题可定位

2.2 抓取工具的参数

# 抓取工具参数设计
register_tool(
    name="fetch_webpage",
    description="抓取网页正文内容,可指定提取 schema",
    parameters={
        "url": str,               # 目标 URL
        "selector": str | None,   # CSS 选择器(可选)
        "schema": dict | None,    # 结构化提取模板(可选)
        "max_chars": int,         # 返回内容上限(默认 5000)
        "render_js": bool,        # 是否渲染 JS(默认 false)
    },
    handler=fetch_webpage,
)

2.3 超时与大小限制

# 1) 抓取超时: 10-20 秒(含下载与渲染)
# 2) 响应大小限制: 默认 2MB(超限截断)
# 3) 返回内容截断: max_chars 限制进上下文的文本
# 4) 重定向跟随: 最多 3 跳,防重定向环
# 抓取是外部依赖,超时与大小限制是基本功

3. 内容提取

网页正文提取是把 HTML 变成「干净的文本」。目标是去掉噪音,保留模型真正需要的正文。

3.1 提取方法

# 提取正文的常用方法
# 1) Readability 算法: 基于文本密度打分找正文区(Mozilla 算法)
# 2) CSS 选择器: 已知目标站点结构时直接选
# 3) 语义标签: article/main/section 优先
# 4) 机器学习提取: 泛化好但重
# 推荐: readability + 语义标签兜底

3.2 噪音清理

# 清理清单
# 1) script/style/noscript: 全删
# 2) nav/header/footer/aside: 通常删
# 3) 广告 iframe/嵌入: 删
# 4) 重复内容(分页导航、评论模板): 去重
# 5) 空白与排版: 归一化
# 清理得越干净,token 用越省、答案越准

3.3 提取结果格式化

# 返回给模型的格式
# 1) 标题 + URL + 抓取时间
# 2) 正文文本(按 max_chars 截断)
# 3) 可选: 阅读时间估计
# 4) 失败信息: 404/超时/被拒 → 清晰报错
# 别返回原始 HTML——模型要的是"页面讲了什么"

4. 结构化输出

有些场景模型需要的是「数据」而非「文章」:商品价格、招聘信息、比赛比分。用 schema 让抓取工具直接输出结构化 JSON,比模型从大段文本里找字段省 token 且更准。

4.1 schema 驱动的提取

{
  "url": "https://example.com/products/123",
  "schema": {
    "title": {"type": "text", "selector": "h1.product-title"},
    "price": {"type": "number", "selector": "span.price", "transform": "strip_currency"},
    "stock": {"type": "text", "selector": "div.stock-status"},
    "specs": {"type": "table", "selector": "table.specs"}
  }
}

4.2 提取引擎实现

async def extract_by_schema(doc, schema):
    result = {}
    for field, conf in schema.items():
        nodes = doc.cssselect(conf["selector"])
        if not nodes:
            result[field] = None
            continue
        text = nodes[0].text_content().strip()
        if conf.get("transform") == "strip_currency":
            text = re.sub(r"[¥$€,\s]", "", text)
        if conf["type"] == "number":
            result[field] = float(text) if text else None
        else:
            result[field] = text
    return result

4.3 schema 的边界

# 1) schema 由用户/调用方给定,模型可生成
# 2) 提取失败字段返回 null(诚实)
# 3) 页面结构变化 → 提取失败 → 提示"页面结构不匹配"
# 4) 无法匹配时回退全文提取
# 结构化提取"猜错字段"时诚实失败,别编数据

5. robots 与合规

抓取不是「能抓到就能抓」。合规边界包括 robots.txt、站点条款、频率控制。这是工程问题也是法律问题。

5.1 robots.txt 处理

# 抓取前检查 robots.txt
# 1) 解析 robots.txt(Allow/Disallow 规则)
# 2) 明确 User-agent(MCP 抓取器标识自己)
# 3) 被 Disallow 的路径不抓
# 4) 抓取日志记录 robots 决策(合规可审计)
# 遵守 robots.txt 是"礼貌"与"合法"的基础
# robots 检查最小实现
from urllib.robotparser import RobotFileParser

def robots_allowed(url: str, user_agent: str = "McpFetcher/1.0") -> bool:
    rp = RobotFileParser()
    rp.set_url(robots_url_for(url))
    rp.read()
    return rp.can_fetch(user_agent, url)

5.2 站点条款与授权

# 1) 公开文档/新闻: 一般可抓,仍遵守 robots
# 2) 有 API 的站点: 优先用 API(官方通道)
# 3) 登录内容/私有数据: 无授权不抓
# 4) 明确 ToS 禁止抓取(如部分社交站): 尊重并跳过
# 授权边界: 有没有权利抓,比能不能抓到更重要

5.3 频率控制

# 1) 每域名最小间隔(如 1-2 秒)
# 2) 并发抓取上限(全局)
# 3) 尊重 Retry-After / 429 响应
# 4) 突发任务排队而不是并发轰炸
# 温和频率: 既能工作,又不给人当"攻击者"的机会

6. 反爬边界

反爬机制存在是为了保护站点。MCP 抓取工具应当避免与反爬系统对抗——那是猫鼠游戏,也是合规风险。

6.1 该做的与不该做的

# 不做
# 1) 绕过验证码(打码平台/识别)
# 2) 伪造指纹绕过风控
# 3) 高频请求打垮站点
# 4) 破解登录/付费墙
# 做
# 1) 遇到反爬返回"被拒,换其它方式"
# 2) 提供替代路径(官方 API/其他来源)
# 3) 放慢或停止
# 反爬被触发是"信号",不是"挑战"——退让是专业

6.2 风控应对

# 被 403/验证码拦下时
# 1) 明确返回"该站点启用了反爬保护"
# 2) 不自动重试轰炸(越试越黑)
# 3) 模型降级: 换搜索摘要 / 换来源 / 告知用户
# 4) 用户手动提供内容(粘贴正文)也是合法路径
# 诚实降级 > 偷偷绕墙

6.3 来源多样性与验证

# 多来源抓取的验证
# 1) 同一事实多来源交叉验证
# 2) 返回来源 URL 与抓取时间(可溯源)
# 3) 标注"信息来自网页,可能有误/过时"
# 模型引用网页信息要像引用资料一样严谨

有些页面需要登录态或会话。会话管理有状态,也就有隔离与安全要求。

7.1 会话模型

# 1) 无状态默认: 每次抓取独立(不携带 Cookie)
# 2) 有状态可选: 用户提供 Cookie/Token(显式授权)
# 3) 会话隔离: 每个 MCP 会话独立 Cookie 容器
# 4) 凭证不进日志/审计
# 默认无状态,需要登录的页面走显式授权
# 1) Cookie 只存服务器内存(或加密存储)
# 2) 不跨会话共享用户 Cookie
# 3) 会话结束清除
# 4) 不把第三方站点凭证用于其它用途
# 会话凭证是高敏数据,按密钥级别对待

8. JavaScript 渲染页面

现代网页大量由 JS 渲染。要不要上无头浏览器,取决于页面是否真的需要。

8.1 何时需要渲染

# 需要 JS 渲染的迹象
# 1) 内容由前端框架(React/Vue)动态填充
# 2) 滚动加载(无限滚动列表)
# 3) 登录后才渲染
# 不需要的迹象
# 4) 内容在 HTML 源码里(SSR/静态)
# 判断: 先抓原始 HTML 看正文在不在,在就不用渲染

8.2 无头浏览器方案

# 用 Playwright 渲染(仅按需启用)
async def fetch_rendered(url: str, wait_ms: int = 1500):
    browser = await p.chromium.launch()
    page = await browser.new_page()
    await page.goto(url, timeout=15000)
    await page.wait_for_timeout(wait_ms)  # 等 JS 执行
    html = await page.content()
    await browser.close()
    return extract_readable(html)

8.3 渲染的代价

# 1) 资源开销: 无头浏览器吃 CPU/内存,并发受限
# 2) 速度慢: 比纯 HTTP 慢 5-10 倍
# 3) 更易触发反爬: 浏览器指纹识别
# 策略: 默认关,需要时显式传 render_js=true
# 能用静态抓就别上浏览器——省资源也少惹风控

9. 生产实践

9.1 抓取代理与基础设施

# 1) 抓取代理(住宅/数据中心)用于地理与频率控制——但勿用于反爬对抗
# 2) 缓存抓取结果(同 URL 短 TTL,避免重复抓)
# 3) 抓取超时与重试(瞬时失败重试 1 次)
# 4) 请求头规范(声明 User-agent)
# 基础设施服务"正常抓取",不服务"对抗"

9.2 监控与审计

# 指标
# 1) 抓取成功/失败率(按域名)
# 2) 反爬触发次数(403/验证码)
# 3) 平均响应时间
# 4) robots 拒绝次数
# 审计
# 抓取日志: URL、时间、来源、robots 决策、结果
# 反爬触发上升 → 检查是否被当攻击工具

9.3 工具设计检查清单

# ☐ 流水线: fetch → parse → clean → extract → structure
# ☐ robots.txt 检查 + 日志
# ☐ 频率控制(域名级间隔 + 全局并发)
# ☐ 超时 + 大小 + 重定向限制
# ☐ 结构化 schema 提取
# ☐ 默认无状态,登录走显式授权
# ☐ 反爬触发 → 诚实降级而非对抗
# ☐ 缓存 + 监控 + 审计

10. 常见陷阱

  • 不查 robots:抓了不该抓的路径,合规风险——抓前解析 robots.txt。
  • 对抗反爬:绕过验证码、伪造指纹——猫鼠游戏且违法,退让并降级。
  • 原始 HTML 进上下文:几 MB 源码 token 爆炸——提取正文再返回。
  • 无频率控制:并发轰炸触发封禁——域名级间隔 + 全局并发上限。
  • 登录态乱共享:用户的 Cookie 跨会话泄漏——会话隔离 + 内存存储。
  • 动辄开无头浏览器:静态页也渲染,慢且惹风控——按需启用。
  • 无缓存:同一 URL 反复抓——短 TTL 缓存。
  • JS 渲染后超时:页面加载不完——等待策略 + 超时兜底。

11. 总结

网页抓取 MCP 工具的工程核心是流水线化提取、结构化输出、合规克制:用「取回 → 清理 → 提取 → 结构化」的流水线把网页变成干净的正文与 JSON,用 robots 检查、频率控制与来源验证守住合规底线,用静态优先、JS 渲染按需的决策控制成本,用诚实降级应对反爬而不是对抗。抓取工具让模型能「看实时网页」,但它的价值建立在尊重站点规则与来源可溯源之上——会抓是能力,知道什么能抓、什么时候该停,才是专业。一句话:抓取要快、要准,更要站得住脚。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「AI工程」更多文章

  1. MCP 多语言 SDK 生态:Python、Go、Rust 与自定义 SDK
  2. MCP 成本与 Token 优化:预算、缓存、批处理与降级
  3. MCP 文件系统工具:安全边界、路径隔离与流式处理