
如果你做过基于大模型的 RAG 知识库或者用过 AI 搜索类 Agent大概率会遇到一个很别扭的场景模型从网页里拿到的内容不是正文而是导航菜单、Cookie 弹窗、广告位、猜你喜欢和评论区。明明是高质量的文章喂给模型之后答案却像在“噪音堆里翻线索”。这件事的本质不是模型不够聪明而是 Web 本身就不是为 AI 设计的。浏览器里的 HTML 是给人眼看、给人手点开发的AI 读到的是未经整理的结构碎片。今天一群工具开始尝试改变这个局面Scrunch 就是其中一个非常典型的代表。它的核心问题很直接把人读的网页重写成 AI 能直接消化和操作的内容。这篇文章不打算复述某一个项目的手册而是围绕“Rewriting the Web for AI”这个判断讲清楚三条事为什么 Web 对 AI 不友好重塑 Web 具体有哪几个技术层次以及我们能上手怎么做。如果你在做 RAG、AI Agent、爬虫工程化或者只是想让自己的网站更容易被 AI 引用这篇文章值得读完并收藏。先说一个明确的判断让 Web 重新适配 AI不是把网页改成纯文本而是做一次系统性的“结构重写”。下面展开讲。1. 这篇文章真正要解决的问题先说痛点。我见过不少团队做 AI 产品时第一步不是调模型而是“清洗网页数据”。他们写大量正则表达式删标签、去广告、去导航、拼正文最后得到的还是一份不太干净的 Markdown。问题出在哪出在我们默认了“Web 内容 一段可抓取的 HTML”。但现实是现代网页的 HTML 是浏览器渲染的结果其中混杂了大量视觉布局和交互逻辑。AI 不关心视觉它关心的是信息实体这篇文档讲什么、关键结论是什么、有哪些结构可引用。HTML 本身提供的信息密度太低。Scrunch 这类工具背后代表的方向就是要在 Web 和 AI 之间加一层“重写层”。它要做的事不是简单地把 HTML 转成文本而是把网页里的噪音过滤掉把信息重新组织和表达让大模型能以更低的成本读取、理解和调用。谁最该读这篇文章正在做 RAG 知识库但被网页内容清洗折磨的开发者做 AI Agent希望 Agent 能直接“读懂网页”而不仅是搜索链接的人做垂直搜索、内容聚合、网页正文抽取服务的后端工程师以及那些希望自己的网站内容可以被 AI 引用、被 Agent 推荐给用户的前端或站长。读完这篇文章你至少能回答三个问题Web for AI 到底要改造什么改造分哪几个层次如何用最小代码跑通一个“网页转 AI 友好内容”的服务。2. “Rewriting the Web for AI”到底是什么意思Scrunch 这个标题有一个关键动作Rewriting重写。它不只是抽取也是重写。要理解这个动作先要理解两个对象——人和 AI 读取网页的差异。人看网页看的是视觉层级。标题大不大、排在哪个位置、颜色是否突出、导航是否有下拉都会影响人对信息的判断。AI 看网页看到的通常是一段 DOM 结构、一堆标签、文本和属性。它没有“视觉注意力”的概念只会按标签结构推断语义。所以在传统 Web 里AI 天然处于劣势。所谓“Rewriting the Web for AI”本质是做三件事把“面向渲染”的 HTML 重写成“面向理解”的结构化文本把“人眼识别”的页面信息重写成“模型可直接引用”的语义单元把“页面浏览”的交互方式重写成“Agent 可调用”的服务接口。这里有一个很容易误会的点很多人以为重写网页就是把 HTML 标签全部剥掉。实际上真正的重写是“重新组织语义单元”。比如剥掉标签之后你仍然要能识别出一篇文章的标题、作者、发布时间、正文、要点和小结。这些信息是人读网页时会自动获取的AI 缺的就是这套自动识别能力。Scrunch 这类工具的立意恰好就是在这个语义化组织上做文章。说得直白一点Web 原有的信息是以“视觉页面”为单位的AI 时代需要的信息是以“语义文档”和“可操作接口”为单位的。从页面到文档、从读取浏览到调用交互这是一次信息组织的范式变化。所以不要觉得“Rewriting the Web for AI”是前端视觉重构。它更像是给 Web 内容建立了一套面向 AI 的“数据协议”决定哪些信息被保留、哪些被丢弃、按什么结构组织、暴露哪些调用能力。3. 为什么大量 Web 内容对 AI 不友好只看概念不够必须落到真实场景。我用三类最常见的问题来说明。第一类是噪音型页面。一个普通的文章页里面往往有页头导航、面包屑、侧边栏推荐、文章标签、作者介绍、评论列表还有一堆跟踪脚本产生的元素。如果把这些内容全部交给 AI模型的上下文窗口很快就占满了而且重要信息可能被淹没。做过 RAG 的人都知道检索召回时如果抓进大量无用文本回答质量会明显下降。第二类是动态渲染型页面。很多现代网站用前端框架页面内容是 JavaScript 动态渲染出来的。常规爬虫拿到的是空壳 HTML正文根本不在里面。要让 AI 拿到有效内容必须在爬取阶段做浏览器渲染或者要求后端提供预渲染版本。这已经不是“清洗”问题而是“渲染链路”问题。第三类是低语义型页面。大量老系统用 div 堆出整个页面所有元素都是div和span没有article、main、aside这些语义化标签。AI 想通过标签推断内容层级时一无所获。下面用一小段 HTML 对比说明。!-- 低语义结构AI 很难判断什么是正文 -- div classwrap div classheader div classnav首页/div div classnav关于/div /div div classmain div classtitle为什么 Web 需要为 AI 重写/div div classdesc本文讨论 Web 语义化对 AI 的影响。/div div classcontent 第一段正文内容…… /div /div div classside热门推荐/div div classfooter版权信息/div /div!-- 语义化结构AI 更容易理解 -- header nav首页 / 关于/nav /header main article h1为什么 Web 需要为 AI 重写/h1 p classsummary本文讨论 Web 语义化对 AI 的影响。/p p第一段正文内容……/p /article /main aside热门推荐/aside footer版权信息/footer人眼能通过位置和样式判断哪个是正文但原始 HTML 给 AI 的信号太弱。如果页面再叠加样式、脚本和跟踪代码AI 能获取的有效信号就更少了。理解这个现状你就能明白为什么“重写 Web”不是小题大做。4. 重塑 Web 的三个具体层次说清楚了现状接下来拆解解决方案。我认为“为 AI 重写 Web”需要覆盖三个层次缺一不可。4.1 内容抽取层从 HTML 到干净正文这一层解决“噪音太多”的问题。思路是从 HTML 里识别出主要内容块剔除导航、页脚、侧边栏等无关模块。经典实现思路类似浏览器的阅读模式遍历 DOM通过标签、类名、文本密度和标点符号密度判断哪些节点更像正文。近些年的规则引擎普遍采用可读性算法和机器学习模型的混合方案。这一层是基础。没有干净的正文后面所有工作都无从谈起。实际工程中内容抽取不只是简单去掉标签还要处理编码、残缺 HTML、弹窗遮罩、懒加载图片等边界情况。4.2 语义化与结构化层让内容自带逻辑这一层解决“信息层级不明”的问题。目标是把网页内容组织成 AI 更容易读取的结构化格式。常见做法包括使用语义化 HTML 标签强化主区域划分使用 JSON-LD 和 Schema.org 标记文章、作者、发布时间等实体信息提供面向 AI 的文本摘要版本或者标准化的 Markdown 输出参考 llms.txt 这类社区倡议思路为站点建立对 LLM 友好的说明文件为 Agent 提供纯文本入口去掉 JS 和视觉效果影响。这一层的关键判断是AI 时代语义不再只服务 SEO还要服务模型的召回和引用。如果你的页面有清晰的article、JSON-LD 和摘要AI Agent 在回答用户时会更容易准确引用你的内容。4.3 协议与服务层从浏览变成调用第三层解决“只能看不能操作”的问题。过去的 Web 是以链接导航为核心的AI Agent 要完成一个任务往往要模拟人一样多次点击、跳转。这种方式不仅慢而且容易失败。更高效的方式是把网页能力暴露成语义化接口让 Agent 通过工具调用完成操作。这里要提一下 MCPModel Context Protocol它本质上就是一套让 AI Agent 调用外部工具和数据的标准化协议。当网站暴露 MCP 接口后Agent 不需要解析 HTML而是直接调用服务获取结构化结果。可以这么理解传统网页是“给人操作的表单”MCP 是“给 Agent 调用的 API”。这三层不是互相替代而是组合演进层次解决的核心问题典型输出核心角色内容抽取层页面噪音太重干净正文 / Markdown后端、数据工程师语义化结构化层信息结构不明显语义标签 / JSON-LD / 摘要前端、站长、后端协议服务层无法程序化操作MCP / API / 可调用工具平台方、Agent 开发者5. 可落地的实践做一个网页转 AI 友好内容的最小服务理论讲完现在动手。我们用 Python 实现一个最小的“网页转 AI 友好内容”服务。它接收一个 URL抓取网页去噪抽取正文转成结构化 Markdown 和摘要再通过 HTTP 接口返回。这个服务可以作为 RAG 采集链路或者 Agent 网页读取模块的最小原型。说明一下以下代码依赖运行时会自动安装本文不写死版本号。如果你用的 Python 环境比较新请以实际安装结果为准。整体思路比具体版本更重要。5.1 环境准备建议使用 Python 3.10 以上版本。创建虚拟环境mkdir web-for-ai cd web-for-ai python -m venv venv source venv/bin/activate安装依赖pip install fastapi uvicorn requests beautifulsoup4 lxml四个依赖的分工fastapi和uvicorn提供 HTTP 服务和接口requests负责抓取网页beautifulsoup4和lxml负责解析 HTML、清洗标签。5.2 实现内容抽取和重写逻辑在项目目录下创建processor.py实现一个简单的正文抽取类。# 文件路径web-for-ai/processor.py import re import html from urllib.parse import urljoin import requests from bs4 import BeautifulSoup class AIWebProcessor: 把网页抽取为 AI 友好的 Markdown 结构。 def __init__(self, timeout10): self.timeout timeout self.headers { User-Agent: Mozilla/5.0 (compatible; AIWebProcessor/1.0; educational) } def fetch(self, url: str) - str: 抓取网页 HTML返回文本。 resp requests.get(url, headersself.headers, timeoutself.timeout) resp.raise_for_status() return resp.text def extract_title(self, soup: BeautifulSoup) - str: 优先取 h1再取 title 标签。 h1 soup.find(h1) if h1 and h1.get_text(stripTrue): return h1.get_text(stripTrue) title_tag soup.find(title) return title_tag.get_text(stripTrue) if title_tag else def extract_main_text(self, soup: BeautifulSoup) - str: 简单策略优先取 article/main 标签其次取 body。 container soup.find(article) or soup.find(main) or soup.body or soup # 去掉脚本、样式、导航、页脚、评论等噪音节点 for tag in container.find_all( [script, style, nav, footer, aside, form, noscript] ): tag.decompose() lines [] for element in container.find_all([h1, h2, h3, h4, p, li]): text element.get_text(stripTrue) if not text: continue lines.append(text) if lines: return \n\n.join(lines) # 兜底方案取纯文本并压缩空白 raw_text container.get_text(separator\n) cleaned_lines [line.strip() for line in raw_text.splitlines() if line.strip()] return \n\n.join(cleaned_lines) def process(self, url: str) - dict: 完整流程抓取 - 解析 - 结构化。 page_html self.fetch(url) soup BeautifulSoup(page_html, lxml) title self.extract_title(soup) content self.extract_main_text(soup) # 生成行数有限的摘要便于 Agent 快速判断内容价值 summary_lines content.split(\n)[:5] summary \n.join(summary_lines) # 输出一个简单的、AI 友好的 Markdown 结构 markdown f# {title}\n\n{content}\n return { url: url, title: title, summary: summary, content: content, markdown: markdown, char_count: len(content), }这个类做的事情不复杂但已经覆盖了三个关键操作抓取网页并设置合理的 User-Agent优先定位article或main剔除常见噪音节点提取标题和正文输出 Markdown。对生产环境来说你可能还要加入阅读时长估算、关键词提取、章节大纲生成等能力但最小原型这样已经够了。5.3 暴露 HTTP 接口在项目目录下创建main.py# 文件路径web-for-ai/main.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from processor import AIWebProcessor app FastAPI(titleWeb for AI 转换服务) processor AIWebProcessor() class WebRequest(BaseModel): url: str app.get(/health) def health(): return {status: ok} app.post(/ai/article) def to_ai_friendly(req: WebRequest): 输入网页 URL输出 AI 友好的结构化内容。 if not req.url.startswith((http://, https://)): raise HTTPException(status_code400, detailURL 格式不正确) try: result processor.process(req.url) except Exception as exc: raise HTTPException(status_code502, detailstr(exc)) return result这里有一个很重要的安全点这个服务接收任意 URL存在 SSRF 风险。实际部署时不要把服务直接暴露到公网如果要暴露必须限制目标域名范围、配置内网地址拦截、增加鉴权和配额控制。下面的最佳实践部分还会展开。5.4 启动与调用先启动服务uvicorn main:app --reload --port 8000然后打开新终端调用curl -X POST http://127.0.0.1:8000/ai/article \ -H Content-Type: application/json \ -d {url: https://example.com/article}返回结果大概是这样的结构字段按处理器里的定义{ url: https://example.com/article, title: 网页标题, summary: 第一行内容\n第二行内容, content: 正文全文, markdown: # 网页标题\n\n正文全文, char_count: 1200 }6. 运行验证与效果观察拿到上面的返回结果后先判断三步字段title是否正确提取如果提取的是站点名而不是文章标题说明页面没有h1或title不够准确字段char_count是否合理如果只有几十个字大概率是页面是纯 JS 渲染requests抓不到正文字段markdown的正文是否完整如果混入大量导航文字需要调整extract_main_text的噪音标签列表。如果第二步失败说明这个页面是动态渲染的需要换成无头浏览器方案。比如 Playwright 或 Puppeteer先把页面渲染完成再交给BeautifulSoup做语义清洗。这是工程上最常见的升级路径尤其是目标站点是大量用 Vue、React 搭建的内容平台时。如果你接入 RAG 流水线建议把这个接口当作“文档加载器”进一步把正文内容按标题层级拆成 chunk再交给 embedding 模型。拆分的粗细要根据模型上下文窗口和检索粒度来调整一般控制在 300 到 800 字之间比较稳妥。7. 常见问题与排查思路在实践过程中你大概率会遇到下面这些问题问题现象可能原因排查方式解决方案返回 403 或请求被拦截目标站点有反爬策略User-Agent 或访问频率被识别查看响应头和状态码信息确认是否是反爬返回降低请求频率使用真实浏览器 UA遵守 robots.txt 和站点条款char_count很小目标站点是前端 JS 动态渲染静态请求拿不到正文用 curl 直接看下载的 HTML 里是否有正文改用 Playwright 渲染后抽取正文混入导航和评论噪音标签列表不完整页面没有标准 article/main 标签打印抽取后的纯文本定位噪音来源扩展噪音标签和容器类名或使用可读性算法库中文乱码页面编码声明与实际编码不一致检查响应头里的 charset 和 meta 标签使用requests的resp.encoding根据内容指定编码请求超时目标站点响应慢查看服务日志中的超时时间调大超时时间或加入重试和熔断机制服务被内网地址攻击接口接收任意 URL存在 SSRF 风险检查访问日志中是否出现异常域名增加域名白名单禁止私网 IP增加鉴权与限流这里面最常见的坑有两个。第一个是“抓到了内容但抓错了位置”等你把 Markdown 喂给模型后才发现答案很偏。第二个是“动态页面问题”静态请求只能拿到空壳这在现代前端项目里非常普遍。8. 最佳实践与工程建议最后讲几点生产环境里真正重要的工程建议。8.1 对网站侧从语义 HTML 开始如果你的网站是面向内容消费的改造顺序建议是用main、article、aside、nav等语义化标签重构页面结构在关键内容区域使用 JSON-LD 标记文章标题、作者、发布时间、摘要和主图对 SPA 页面做服务端渲染或预渲染至少保证基础内容不经 JS 也能读到在站点根目录维护一份对 LLM 友好的站点说明文本让搜索型 Agent 能快速了解站点主题和内容边界。这些改动的直接收益是你的内容更容易被 AI 搜索正确引用被 Agent 推荐给用户。对内容类平台来说这已经不只是 SEO而是“AEO”——答案引擎优化。8.2 对采集侧合法、限速、去重写爬虫时首先检查目标站的 robots.txt 和服务条款只采集你被允许访问的内容。控制抓取频率设置合理的重试退避。对采集到的内容做 URL 去重和内容指纹去重避免重复入库。抓取在线内容用于商业项目前务必评估版权风险。8.3 对 AI 侧清洗、分块、验证在用大模型处理网页内容时别把清洗工作全交给模型。前端清洗能去掉的噪音就不要浪费模型上下文。内容进入知识库前做好分块和元数据标注例如来源 URL、发布时间、标题、章节路径。后续检索环节可以根据这些元数据做过滤提高召回准确率。同时建议保留原文和清洗后文本的对应关系出了问题可以回溯。8.4 安全边界必须落地凡是接收 URL 的服务都要当作高权限服务来设计。具体来说限制可访问的域名白名单过滤私网 IP、环回地址、云元数据地址对抓取结果做大小限制防止超大页面拖垮服务增加接口鉴权、调用频率限制、请求超时和熔断不要在日志里记录正文内容只记录 URL、状态和耗时。8.5 质量监控生产系统要对抽取质量做人工抽查和自动评估。可以设置每周抽样一批 URL做一个“正文是否完整、有无残留导航、标题是否正确”的小评测集。模型迭代过程中这些评测集能帮你判断哪些改动是正向的。9. 总结与后续学习方向回到开头的问题。Scrunch 代表的“Rewriting the Web for AI”方向真正要解决的是信息组织方式的代际切换。Web 以前是给眼睛看的未来要同时给机器和模型读。这不只是前端工程师的事也不只是 AI 工程师的事而是内容生产、前端渲染、后端接口和数据工程共同的改造任务。对个人开发者来说最好的切入方式是从一个小页面开始。选一个你常用的内容网站把它接入上文这个最小服务看看抽取效果如何然后给自家网站加上语义化标签和 JSON-LD最后再考虑把你的网站能力暴露成 Agent 可以调用的接口。每一步都不难但组合起来就是一次让 Web 重新适配 AI 的实际落地。后续值得继续深入的方向包括MCP 协议如何让 Agent 直接操作网站的某个业务功能如何用可读性算法提升复杂页面的抽取精度以及如何把结构化后的内容更好地接入 RAG 和 Agent 工作流。建议你把这篇文章收藏起来按里面的步骤先把最小服务跑通再逐步往生产级推进。