作者:河南晨讯云文化科技有限公司技术负责人。文中配置来自政务、教育类站点的实际部署与线上排查,涉及的具体域名与业务信息已做脱敏处理。本文只讲技术,不含产品与服务推广。
页面写得再好,爬虫拿不到就等于没写。但“抓取”这件事在国内的技术讨论里经常被简化成一句“做好 SEO 就行”,实际上针对生成式引擎的抓取配置有独立的细节:哪些 UA 要放行、llms.txt 到底有没有用、JSON-LD 写成什么样才会被解析、CDN 在背后悄悄拦了多少请求。
这篇文章把三件事拆开讲清楚,每部分都给可直接复制的配置和验证命令。
一、先认清楚:抓你站点的到底是什么
不同爬虫的目的完全不同,放行策略也应该不同。混在一起处理,是配置错误的第一个来源。
| UA | 归属 | 用途 | 是否影响搜索收录 |
GPTBot |
OpenAI | 模型训练语料 | 否 |
OAI-SearchBot |
OpenAI | 搜索索引 | 是(影响 ChatGPT 搜索可见性) |
ChatGPT-User |
OpenAI | 用户提问时实时访问 | 是(影响实时引用) |
ClaudeBot |
Anthropic | 训练与检索 | 是 |
Claude-User |
Anthropic | 用户实时访问 | 是 |
PerplexityBot |
Perplexity | 检索索引 | 是 |
Google-Extended |
Gemini 训练 | 否(不影响 Google 搜索排名) | |
Bytespider |
字节跳动 | 训练与检索 | 是(豆包) |
Baiduspider |
百度 | 搜索索引 | 是(文心) |
CCBot |
Common Crawl | 公共语料库 | 间接(大量模型用其数据) |
Applebot-Extended |
Apple | Apple Intelligence 训练 | 否 |
meta-externalagent |
Meta | AI 训练 | 否 |
两个容易搞混的点:
第一,Google-Extended 和 Google 搜索是两回事。 在 robots.txt 里 Disallow 它,只会阻止 Gemini 的训练使用,不会影响页面在 Google 搜索结果里的排名。很多人因为错误理解这一条,把整站都挡了。
第二,GPTBot 和 OAI-SearchBot 要分开对待。 只想让你的内容出现在 ChatGPT 的回答里、但不想被拿去训练,正确做法是放行 OAI-SearchBot 和 ChatGPT-User、屏蔽 GPTBot。反过来只放 GPTBot 是没意义的——训练语料和检索索引是两条独立管线。
UA 字符串会随版本更新变化(比如
GPTBot/1.0到GPTBot/1.1),配置时用产品名而非完整版本号,以各厂商官方文档为准。
二、robots.txt:三个最常见的致命错误
错误一:Disallow: / 把所有人都挡了
# 错误示范 User-agent: * Disallow: /
这一行会挡掉所有爬虫,包括 Googlebot 和 Baiduspider。搜索结果里页面全部消失,通常就是从这里开始的。
错误二:挡掉了 CSS 和 JS
# 错误示范 User-agent: * Disallow: /static/ Disallow: /assets/
Google 和部分 AI 爬虫需要渲染页面才能理解内容。把样式表和脚本挡掉,爬虫看到的是一个没有排版的骨架,正文可能整块丢失。
错误三:以为 robots.txt 是访问控制
robots.txt 是君子协定,不是防火墙。恶意爬虫完全不看它。真正的访问控制要靠 WAF、鉴权或 IP 限流。反过来说,你想拦的爬虫,robots.txt 未必拦得住;你不想拦的,倒可能被误伤。
一份可用的配置
# 放行生成式引擎的检索类爬虫 User-agent: OAI-SearchBot Allow: / User-agent: ChatGPT-User Allow: / User-agent: ClaudeBot Allow: / User-agent: PerplexityBot Allow: / User-agent: Bytespider Allow: / # 不允许用于模型训练(按需取舍) User-agent: GPTBot Disallow: / User-agent: Google-Extended Disallow: / # 其余爬虫按站点策略 User-agent: * Allow: / Disallow: /admin/ Disallow: /api/private/ Disallow: /*?sessionid= Sitemap: https://example.com/sitemap.xml
注意:User-agent 的匹配规则各家实现略有差异(多数不区分大小写),但建议按官方文档原样书写,不要自创大小写变体。
三、llms.txt:有用,但先别指望它
它是什么
llms.txt 是 2024 年 9 月由 Jeremy Howard(Answer.AI)提出的一个提案,站点根目录下放一个 Markdown 文件,用简洁的结构告诉大模型“我这个站点是干什么的、核心内容在哪”。
它的定位很像给 AI 看的 sitemap,但更强调语义——不是罗列所有 URL,而是挑出真正值得读的内容。
格式规范
# 站点名称 > 一句话说明站点定位(20-60 字),这段会作为摘要被读取 补充说明段落(可选),交代背景、面向谁、内容范围。 ## 核心内容 - [文章标题](https://example.com/page1):一句话说明这篇讲什么 - [文章标题](https://example.com/page2):一句话说明 ## 可选 - [次要内容](https://example.com/other):说明 ## 关于 - 主体名称、所在地、主营业务等实体信息
结构要点只有四条:
- 一级标题是站点名,唯一一个;
- 紧接着一个引用块作为摘要,这是最可能被读取的部分;
- 二级标题分区,
## 可选区里的链接表示“次要,可跳过”; - 每个链接后面用冒号加一句说明,说明比链接本身更重要。
关于“到底有没有用”
这里必须说实话:目前没有公开证据表明 OpenAI、Anthropic、Google 的爬虫会主动请求 /llms.txt。
它的现状更接近这样:
- 是一个正在被讨论的提案,已有部分工具和服务商支持;
- 部分聚合类、检索类服务开始读取它;
- 对主流大模型厂商,它暂时更像一种声明而非指令。
所以正确的期待是:成本极低(一个静态文本文件),值得加,但不要把它当成能立刻提升引用率的开关。 真正起作用的是页面本身的可抓取性和结构化程度。
另外还有一个变体叫 llms-full.txt,用于放整站全文的合并版本,适合内容量不大的站点。文件体积要控制,几百 KB 以上意义不大。
部署
# 放在站点根目录,能被公开访问 https://example.com/llms.txt # 建议在 robots.txt 里声明(非标准,但部分爬虫会看) # 也可以在 <head> 里加一行 <link rel="llms" href="/llms.txt">
验证:
curl -sI https://example.com/llms.txt | head -5 # 期望返回 200,Content-Type 为 text/plain 或 text/markdown
四、JSON-LD:三件套里最实在的一件
相比 llms.txt,JSON-LD 是已经确定被解析的。搜索引擎用它生成富媒体结果,大模型在检索环节也会利用结构化字段判断实体和事实。
4.1 用 @graph 组织,不要写成一堆独立 script
多个结构化数据块分散在页面里容易产生实体冲突(同一个组织被描述成两个不同实体)。推荐用 @graph 装在一个 script 里:
<script type="application/ld+json"> { "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://example.com/#org", "name": "主体名称", "url": "https://example.com", "logo": "https://example.com/logo.png", "sameAs": [ "https://www.qcc.com/firm/xxxxx.html", "https://weibo.com/xxxxx" ] }, { "@type": "WebSite", "@id": "https://example.com/#website", "url": "https://example.com", "name": "站点名称", "publisher": { "@id": "https://example.com/#org" } }, { "@type": "Article", "@id": "https://example.com/page#article", "headline": "文章标题(建议 110 字符以内)", "description": "文章摘要", "datePublished": "2026-11-09T10:00:00+08:00", "dateModified": "2026-11-09T10:00:00+08:00", "inLanguage": "zh-CN", "author": { "@id": "https://example.com/#org" }, "publisher": { "@id": "https://example.com/#org" }, "mainEntityOfPage": { "@id": "https://example.com/page" }, "about": [ { "@type": "Thing", "name": "主题一" }, { "@type": "Thing", "name": "主题二" } ] } ] } </script>
@id 的作用是让不同块之间能互相引用,形成一张图而不是几个孤岛。sameAs 建议填第三方可核验的页面(企业信息平台、百科、官方社媒),这是建立实体关联最有效的一步。
4.2 FAQPage:注意它的真实效果
{ "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "问题原文,必须与页面可见文字一致", "acceptedAnswer": { "@type": "Answer", "text": "答案原文,同样必须与可见内容一致" } } ] }
三个硬性约束:
- 问答必须在页面上真实可见,不能只写在 JSON-LD 里——这是最常见的违规写法;
- 一个页面一个问题块,不要把所有页面的问答堆到首页;
- Google 在 2023 年 8 月后已大幅收紧 FAQ 富媒体结果的展示范围(主要保留给政府和医疗类权威站点),所以不要指望它能带来搜索结果里的折叠问答。
那它还有什么用?对 AI 检索有用。 结构化的问答对是最容易被直接摘取的形式,即使不显示为富媒体结果,它在语义解析环节仍然有价值。这也是我仍然建议写 FAQPage 的原因——目标不是富媒体,是让模型更容易抽取答案。
4.3 五个高频错误
| 错误 | 后果 | 正确做法 |
dateModified 写成 2026/11/09 |
无法解析 | ISO 8601,带时区 2026-11-09T10:00:00+08:00 |
headline 超过 110 字符 |
被截断或忽略 | 控制在 110 字符内 |
| 问答内容与页面可见文本不符 | 可能被判作弊 | 严格一致 |
| 组织名在不同页面写法不一(全称/简称混用) | 实体分裂,无法关联 | 全站统一,简称放 alternateName |
嵌套层级写错(author 写成字符串) |
字段被丢弃 | 用对象或 @id 引用 |
4.4 校验
# 1. 语法校验(Schema.org 官方) https://validator.schema.org/ # 2. Google 富媒体结果测试 https://search.google.com/test/rich-results # 3. 本地快速验证 JSON 合法性 curl -s https://example.com | \ grep -oP '(?<=<script type="application/ld\+json">).*?(?=</script>)' | \ python3 -m json.tool > /dev/null && echo "JSON-LD 语法合法"
五、抓取成功 ≠ 被引用:中间还差两步
这是最容易被忽略的认知断层。链路其实是四段:
可被抓取 → 被抓取 → 被理解 → 被引用
robots.txt 只管第一段,llms.txt 想影响第三段,JSON-LD 帮第三段。第二段(真的抓了没有)和第四段(引用时说的是不是对的)需要看日志和实测。
用 curl 模拟爬虫
# 模拟 GPTBot 访问首页,只看响应头 curl -A "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.1; +https://openai.com/gptbot)" \ -sI https://example.com/ | head -8 # 期望:200,且没有被 WAF 返回 403
如果本机 curl 返回 200、线上 AI 就是不引用,大概率问题在 CDN 或 WAF,不在 robots.txt。
从日志里看真实抓取
# nginx:统计近 7 天各 AI 爬虫的访问次数 grep -iE "GPTBot|OAI-SearchBot|ClaudeBot|PerplexityBot|Bytespider" \ /var/log/nginx/access.log | \ awk '{print $NF}' | sort | uniq -c | sort -rn | head -20 # 只看被拒绝的请求(状态码 403/429) grep -iE "GPTBot|ClaudeBot" /var/log/nginx/access.log | \ awk '$9 ~ /40[13]|429/ {print $1, $7, $9, $NF}' | head -20
第二种查询特别有用:如果 AI 爬虫的请求大量返回 403,说明拦截发生在服务端而非 robots.txt,这时候再怎么改爬虫配置都没用。
这类查询值得每周跑一次。AI 爬虫的 UA 会随版本更新变化,日志是唯一能确认“它到底来没来”的地方——配置指令写没写对,日志会直接告诉你。
六、CDN 和 WAF:最隐蔽的一道墙
这一条值得单独说,因为它经常是“配置全对但就是不收录”的真正原因。
Cloudflare 从 2024 年 7 月起,对新接入的域名默认拦截 AI 爬虫(通过其 bot 管理功能)。如果你的站点套了 Cloudflare,需要在面板里检查:
- Security → Bots → 确认 AI Scrapers and Crawlers 的处置动作是
Allow还是Block; - 如果用到了“Under Attack Mode”,它会拦掉几乎所有非浏览器 UA,包括 AI 爬虫;
- 部分托管型 WAF(阿里云、腾讯云、AWS 的托管规则)也有 bot 管理模块,默认策略可能包含“拦截已知爬虫”。
检查方法:
# 对比不同 UA 的响应,最直观 for ua in "GPTBot/1.1" "ClaudeBot/1.0" "Mozilla/5.0"; do code=$(curl -A "$ua" -s -o /dev/null -w "%{http_code}" https://example.com/) echo "$ua -> $code" done
如果 GPTBot 返回 403 而普通 UA 返回 200,问题就在这一层。
七、一份可执行的检查清单
按顺序过一遍,基本能定位绝大多数问题:
robots.txt没有Disallow: /的全局误伤- CSS/JS 目录未被屏蔽
- 检索类 AI 爬虫(OAI-SearchBot / ClaudeBot / PerplexityBot / Bytespider)已放行
- 已明确训练类爬虫(GPTBot / Google-Extended)的取舍策略
sitemap.xml可访问且已提交- 核心正文在未执行 JS 的情况下可见(用
curl看原始 HTML 是否含正文) - 关键信息(价格、地址、参数)不只是图片
- 页面有
Article或对应类型的 JSON-LD dateModified为 ISO 8601 格式- 组织信息全站统一,
sameAs指向可核验的第三方页面 - JSON-LD 通过 validator.schema.org 校验
- CDN/WAF 未拦截 AI 爬虫(用 curl 不同 UA 对比验证)
- 日志中能看到 AI 爬虫的 200 响应记录
八、几个反复出现的误区
“加了 llms.txt 就能被 AI 引用”——不能。它目前更接近声明性的约定,不是开关。
“结构化数据越多越好”——不是。字段冗余和被忽略的字段会干扰解析,只写与页面真实内容对应的类型。
“页面用 React/Vue 渲染没问题”——对 Google 基本没问题,但对多数 AI 爬虫有风险。它们不保证执行 JS,抓到的可能是空壳。核心内容需要 SSR 或至少有预渲染版本。
“抓到了就会引用”——抓到只是进入候选池。能不能被摘出来,取决于表述是否清晰、事实是否密集、同一组信息在不同页面上是否口径一致。最后这条最容易被忽略:一个页面写“覆盖全省”、另一个写“仅主城区”,模型判断不出哪个可信,可能两个都不用。
配置能解决“读得到”的问题,解决不了“写得好”的问题。两件事都要做,但顺序不能颠倒——先确保抓得到,再谈写得怎么样。
本文涉及的 UA 名称、llms.txt 提案、Cloudflare 默认策略均以各厂商官方文档为准,实际配置前建议复核。文中配置均在真实站点验证,具体业务信息已脱敏。技术问题欢迎在评论区讨论。