AI 爬虫授权规范解读:robots.txt 放行策略、llms.txt 说明文件与内容时效声明三件套

2026-09-16 14:30:27 20 次浏览
robots.txtllms.txtAI爬虫GEO规范解读

上个月有个客户的运维同学问我:"robots.txt 里 Disallow: / 是当年外包写的,现在我们想让 AI 引擎引用内容,是不是把这段删掉就行?"

答案是不行,而且是两个方向都不行。Disallow: / 删掉之后,非 AI 爬虫(比如各种数据采集爬虫)会一起涌进来;不删,AI 爬虫确实拿不到内容——但真正的问题是,很多团队把 robots.txt 当成一道开关,其实它是一份策略文档。这篇把三件事讲清楚:robots.txt 的 AI 放行策略怎么写、llms.txt 到底是什么、以及一个常被忽略但很关键的东西——内容的时效声明。

一、先理解 robots.txt 的消费机制

flowchart TD
    A[爬虫请求 /robots.txt] --> B{解析 UA 匹配组}
    B -->|命中具体 UA 组| C[应用该组规则]
    B -->|未命中| D[应用 User-agent: * 组]
    C --> E{路径匹配 Disallow}
    E -->|匹配| F[放弃抓取]
    E -->|不匹配| G[抓取页面]
    D --> E

三个容易踩的点:

第一,匹配是前缀匹配,不是正则。 Disallow: /products/ 拦的是以该路径开头的所有 URL;想用 * 通配(Disallow: /*?print=)在主流爬虫里是支持的,但属于规范化扩展而非基础规范,不要依赖它做关键策略。

第二,User-agent 组按最长匹配生效,而不是"取最后一个"。 声明 User-agent: GPTBotUser-agent: * 两组时,GPTBot 命中前者,不会叠加后者的规则。所以放行某个 AI 爬虫时,必须在该组里把全部规则写清楚,不能指望继承默认组。

第三,robots.txt 是"请求"不是"强制"。 遵守它是行业惯例,不代表技术约束。真正需要防的抓取压力要用限流与鉴权解决,robots.txt 只负责表达意愿。理解这一点,就不会把它当成安全边界来设计。

1.1 AI 爬虫的 UA 与用途分类

放行策略之前,先分清这些爬虫是干什么的。它们至少分三类,用途不同,策略也就不同:

类别 代表 UA 用途 常见策略
训练语料抓取 GPTBot、ClaudeBot、Google-Extended 进入模型训练数据 视内容策略决定
检索索引抓取 OAI-SearchBot、PerplexityBot、Bytespider 建索引,可带来引用 建议放行
用户触发抓取 ChatGPT-User、Perplexity-User 用户当次提问时实时访问 建议放行

这张表的关键区别在第二和第三类:它们直接决定你的内容能不能被"引用"。第一类只影响未来某次模型训练。很多团队一刀切把 AI 爬虫全拦了,实际上把第二三类也拦掉了,这才是引用率低的直接原因。如果确实不希望内容进入训练语料,只拦第一类即可。

二、放行策略的正确写法

以下是一份可直接改造使用的配置,注释说明了每一段的意图。注意 robots.txt 不支持注释嵌套,# 之后整行忽略。

# ---- 默认组:传统爬虫,放行收录 ----
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /search?
Disallow: /*?session=
Sitemap: https://www.example.com/sitemap.xml

# ---- 检索与实时访问类 AI 爬虫:明确放行 ----
# 单独成组是为了避免规则被默认组覆盖
User-agent: OAI-SearchBot
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: ChatGPT-User
Allow: /

User-agent: Bytespider
Allow: /
Crawl-delay: 1

# ---- 训练语料类爬虫:按内容策略决定 ----
# 若允许进入训练语料,改为 Allow: /
User-agent: GPTBot
Disallow: /

User-agent: ClaudeBot
Disallow: /

User-agent: Google-Extended
Disallow: /

几点说明。Crawl-delay 只有部分爬虫遵守,但写上去没有坏处,配合服务端限流更可靠。Google-Extended 是一个特殊声明,它控制的是 Gemini 等产品的训练与 grounding 用途,与 Googlebot 的搜索收录是两条线,拦它不影响 Google 搜索排名——这一点经常被搞混。

2.1 用日志验证策略是否生效

写完之后必须验证,否则你不知道爬虫有没有来、有没有被规则挡住。下面这段脚本按 UA 分组统计 AI 爬虫的访问情况与响应码分布:

# 环境:Python 3.10+,仅用标准库,输入为 Nginx combined 格式日志
import re
from collections import defaultdict

AI_UA = {
    'GPTBot': '训练抓取',
    'OAI-SearchBot': '检索索引',
    'ChatGPT-User': '用户触发',
    'PerplexityBot': '检索索引',
    'Bytespider': '检索索引',
    'ClaudeBot': '训练抓取',
}

LINE = re.compile(
    r'^\S+ \S+ \S+ \[[^\]]+\] "(?P<method>\S+) (?P<path>\S+)[^"]*" '
    r'(?P<status>\d{3}) \S+ "[^"]*" "(?P<ua>[^"]*)"'
)

def analyze(path: str) -> dict:
    stats = defaultdict(lambda: defaultdict(int))
    with open(path, encoding='utf-8', errors='ignore') as fh:
        for line in fh:
            m = LINE.match(line)
            if not m:
                continue                              # 跳过不规范的行
            ua = m.group('ua')
            for key, kind in AI_UA.items():
                if key.lower() in ua.lower():
                    # 记录该类爬虫的状态码分布,403/404 说明规则或路径有问题
                    stats[f'{key}({kind})'][m.group('status')] += 1
                    break
    return stats

if __name__ == '__main__':
    result = analyze('access.log')
    for name, codes in sorted(result.items()):
        total = sum(codes.values())
        detail = ', '.join(f'{k}:{v}' for k, v in sorted(codes.items()))
        print(f'{name:22} 合计 {total:6d}  状态码 {detail}')

判读方式很简单:如果某类爬虫的请求里出现大量 403,说明你的 robots.txt 与 WAF 规则冲突,愿望和现实不一致;如果某类爬虫一个月零请求,要么它还没发现你的站,要么它的入口(首页、sitemap)被挡了。

三、llms.txt:它不是 robots.txt 的替代品

llms.txt 是一个较新的约定,社区提案见 https://llmstxt.org 。它的形态是一个放在域名根目录的 Markdown 文件,用标题、列表和链接给大语言模型提供一份站点导航与内容说明。值得注意的是:它目前是社区约定,不是搜索引擎或主流 AI 厂商公告的强制标准,所以定位要对——它是"便利设施",不是"通行证"。

它有用吗?在我们做过的项目里的判断是:对已有良好索引的大站,收益有限;对内容结构复杂、入口分散的站点,它能降低模型理解站点结构的成本。以下是一份可直接改写的示例:

# 示例科技

> 工业检测设备制造商,产品覆盖局部放电检测、红外测温与振动分析三大类,服务电力、轨道交通与石化行业。

## 核心内容

- [产品中心](https://www.example.com/products):全部设备型号与参数
- [选型指南](https://www.example.com/guide):按场景与预算选设备
- [技术文章](https://www.example.com/blog):检测原理与现场案例
- [关于我们](https://www.example.com/about):公司资质与服务网络

## 说明

- 内容语言:简体中文;产品参数以产品页为准
- 参数更新时间:2026-09
- 引用要求:引用参数时请以产品页数值为准,避免跨型号混用

最后一段"引用要求"是很多示例里没有、但很实用的部分:把使用边界写清楚,能减少参数被错误拼接进回答的概率。

3.1 robots.txt、llms.txt 与内容时效声明的分工

这三者常被混为一谈,实际职责完全不同:

文件/声明 位置 作用对象 是否强制
robots.txt 域名根目录 抓取行为 行业约定
llms.txt 域名根目录 内容理解与导航 社区约定
内容时效声明 页面内(dateModified 内容新鲜度判断 结构化数据规范

第三个才是影响引用意愿的关键。AI 引擎回答一个会变化的问题(价格、版本、政策)时,倾向于引用有明确时间标记的内容。dateModified 缺失或长期不变,页面就会被判定为"不知道新不新",从而在时效敏感的问题里被跳过。

三者的协作顺序可以用一张决策图概括,这也是排查"内容不被引用"时的排查路径:

flowchart LR
    A[爬虫到达域名] --> B{robots.txt 允许}
    B -->|否| Z[放弃抓取]
    B -->|是| C[抓取页面并解析]
    C --> D{llms.txt 有无导航说明}
    D -->|有| E[按说明定位核心页面]
    D -->|无| F[自行推断站点结构]
    E --> G{dateModified 是否新鲜}
    F --> G
    G -->|新鲜| H[进入引用候选集]
    G -->|陈旧或无| I[时段敏感问题中被跳过]

按这张图排查,能避免把问题归错因:引用为零时,先看 B(有没有被挡),再看 C(内容是否可解析),然后才是 D 和 G。我们遇到过的多数案例停在 B 或 C 上,真正卡在 llms.txt 的很少。另外提醒一点,改动 robots.txt 之后不要让浏览器或 CDN 长期缓存它——不少站点把根目录静态文件设了长缓存,结果策略改了两周爬虫读到的还是旧文件,这类问题在日志里表现为"规则明明放行了,某类爬虫依然零请求"。

四、内容时效声明怎么落地

4.1 三处信号要一致

时效信号在站点里有三个来源,必须一致:页面可见的"最后更新"文字、结构化数据里的 dateModified、sitemap 里的 lastmod。三者不一致时,引擎会倾向相信更保守的那个。

{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "局部放电检测的三种方法对比",
  "datePublished": "2026-03-11",
  "dateModified": "2026-09-08",
  "author": { "@type": "Person", "name": "示例作者" },
  "publisher": { "@type": "Organization", "name": "示例科技" }
}

4.2 用脚本守住一致性

人工核对不可靠,把它做成发布流水线的一环:

# 环境:Python 3.10+,依赖 requests、beautifulsoup4
# 作用:比对页面可见更新日期、JSON-LD 的 dateModified 与 sitemap 的 lastmod
import json
import re
import requests
from bs4 import BeautifulSoup

SITEMAP = 'https://www.example.com/sitemap.xml'

def sitemap_lastmod(url: str) -> str | None:
    xml = requests.get(SITEMAP, timeout=15).text
    pattern = rf'<loc>{re.escape(url)}</loc>\s*<lastmod>([^<]+)</lastmod>'
    m = re.search(pattern, xml)
    return m.group(1)[:10] if m else None           # 归一化到日期粒度

def page_dates(url: str) -> tuple[str | None, str | None]:
    html = requests.get(url, timeout=15).text
    soup = BeautifulSoup(html, 'html.parser')
    visible = soup.select_one('[data-last-updated]')       # 页面可见的更新文字
    visible_date = visible.get_text(strip=True) if visible else None

    modified = None
    for tag in soup.find_all('script', type='application/ld+json'):
        try:
            data = json.loads(tag.string or '{}')
        except json.JSONDecodeError:
            continue
        if isinstance(data, dict) and data.get('dateModified'):
            modified = str(data['dateModified'])[:10]
    return visible_date, modified

def check(url: str) -> list[str]:
    problems = []
    visible, modified = page_dates(url)
    lastmod = sitemap_lastmod(url)
    if modified and lastmod and modified != lastmod:
        problems.append(f'dateModified({modified}) != lastmod({lastmod})')
    if visible and modified and visible != modified:
        problems.append(f'可见更新日期({visible}) != dateModified({modified})')
    if not modified:
        problems.append('缺少 dateModified')
    return problems

if __name__ == '__main__':
    for target in ['https://www.example.com/blog/pd-methods']:
        issues = check(target)
        print(target, 'OK' if not issues else issues)

把它挂到发布流程里,每次内容更新自动校验三个信号是否对齐,比事后靠搜索引擎报错反馈要快得多。

五、误区与趋势

误区一:拦掉所有 AI 爬虫以保护内容。 结果是连同检索类爬虫一起拦掉,AI 渠道的引用归零。想保护的是训练用途,就只拦训练类 UA,两者的边界是可以精确划分的。

误区二:把 llms.txt 当必做项。 它目前没有强制标准地位,纯静态展示站、页面数不多的站点,把 sitemap 与结构化数据做扎实的收益远高于做一个 llms.txt。

误区三:时效声明写成"编译期时间"。 有些站点的 dateModified 由模板统一生成,页面每次渲染都变成当天,看似"很新",实际会让时间信号失去含义;一旦引擎发现时间与内容不匹配,可信度是会下降的。

趋势上可以预判两点:一是 AI 爬虫的授权表达会继续细化,从"整站开关"走向"按内容类型、按用途声明";二是时效信号与实际内容变化的关联会被更严格地核验,靠模板刷新日期来"装新"的空间会越来越小。技术上收个尾:这三件套里,robots.txt 管"能不能来",llms.txt 管"你该怎么读",时效声明管"值不值得引用"——先想清楚每一条要表达什么,再动手写配置。你们的站点是怎么处理 AI 爬虫授权的,欢迎在评论区交流。

参考与延伸

  • robots.txt 规范(RFC 9309):https://www.rfc-editor.org/rfc/rfc9309.html
  • llms.txt 社区提案:https://llmstxt.org
  • Google 搜索中心:robots.txt 使用指南:https://developers.google.com/search/docs/crawling-indexing/robots/intro
  • Schema.org dateModified 定义:https://schema.org/dateModified

关键词:GEO 生成式引擎优化、AI 优化 AIO、robots.txt 规范、llms.txt、dateModified 时效信号、AI 爬虫 UA、RFC 9309、内容索引

🤖
本内容由 AI 辅助生成,经人工校对审核;部分素材、资料来源于公开网络,仅作个人观点分享与交流使用,无任何商业侵权意图。若内容、图片、文字涉及您的合法著作权、版权权益,请联系本人,核实后将第一时间删除、修改相关内容。