robots.txt 与 sitemap.xml 实战:抓取预算分析、Disallow 与 noindex 的互斥陷阱、AI 爬虫取舍

robots.txt 写错了,可能比不写更糟

很多站长对 robots.txt 的态度是"反正搜索引擎自己会找",或者从别处复制一份模板扔到根目录就完事。实际上,robots.txt 是你唯一能主动控制爬虫行为的官方通道,而它写错一次,可能让整站收录腰斩——而且过程完全静默,不会有任何报错。

这篇文章不讲基础语法(网上到处都是),只讲那些真正会让你掉收录、掉流量的具体场景,以及怎么用日志验证你写的规则真的生效了。内容站的 robots.txt 和 sitemap 是一套组合拳,一起讲才完整。

一、先明确 robots.txt 的能力边界

这是最多人误解的地方。必须清楚 robots.txt 能做什么、不能做什么:

  • 它是请求,不是命令。 遵守与否完全取决于爬虫自愿。Google、Bing、百度都遵守,但恶意采集器、SEO 分析工具、AI 训练爬虫(部分)根本不看。
  • 它不能阻止索引。 这是最致命的误解。Disallow 只阻止抓取,不阻止索引。如果站内有其他页面链接到了被 Disallow 的 URL,搜索引擎完全可以只靠外链锚文本把它索引进结果页——只是它抓不到内容,于是显示一个没有摘要的空结果。这叫"无摘要收录",对站点质量是纯负面。
  • 正确做法:想阻止索引用 <meta name="robots" content="noindex">,且该页面必须允许被抓取(否则爬虫看不到这个 meta 标签)。Disallow 和 noindex 不能同时用,这是 SEO 里最经典的互斥陷阱。

所以规则是:

  • 想让它不出现在搜索结果里 → 页面可抓取 + noindex
  • 想让它被爬虫完全忽略(比如后台、API、测试目录)→ Disallow,并且确保站内没有任何链接指向它。

二、内容站 robots.txt 的推荐骨架

下面这份是给个人内容站/博客用的,可以直接改改就用:

User-agent: *
Allow: /

# 后台与管理入口,彻底封闭
Disallow: /admin/
Disallow: /action/
Disallow: /install.php

# 带参数的重复页面,避免抓取预算浪费
Disallow: /*?replyTo=
Disallow: /*?*&replyTo=
Disallow: /*?page=*
Allow: /*?page=1$

# 站内搜索与动态查询,无限组合会产生海量 URL
Disallow: /search/
Disallow: /*?s=

# 功能性路径
Disallow: /feed/
Disallow: /rss.xml
Disallow: /trackback/

# 不想被索引但可被抓取的,用 noindex 处理,不在这里 Disallow

Sitemap: https://www.example.com/sitemap.xml

逐条解释几个容易踩坑的点:

1. 通配符 * 和结尾锚定 $

Disallow: /*?page=* 会拦掉所有带分页参数(含 page=1)的 URL。Allow: /*?page=1$ 前面的 Allow 优先规则把它放出来——这里体现了 robots 的匹配原则:匹配最长的规则优先,长度相同时 Allow 优先。所以 Allow: /*?page=1$Disallow: /*?page=* 更具体时,page=1 是可抓取的。

2. 注意 * 不能匹配 / 吗?

可以匹配,robots 的 * 匹配任意字符序列包括斜杠。这和 shell 不一样,很多人按 shell 的直觉去理解结果就错了。

3. 关于 Crawl-delay

Google 官方已不遵守 Crawl-delay(它有自己的抓取速率算法,在 Search Console 里设置)。Bing 部分遵守。百度基本不看。所以不要在 robots.txt 里写 Crawl-delay 来"限速",那只会给你的日志加一行噪音。真正要限速,用 Nginx 的 limit_req 按 UA 做,这才是有效手段。

4. 关于 AI 爬虫

近两年新增了一批高频爬虫:GPTBot、ClaudeBot、CCBot、Google-Extended、Bytespider、PerplexityBot 等。它们抓取量往往比搜索引擎大得多。是否要拦,取决于你的策略:

# 屏蔽 AI 训练爬虫,节省带宽
User-agent: GPTBot
Disallow: /

User-agent: ClaudeBot
Disallow: /

User-agent: CCBot
Disallow: /

User-agent: Bytespider
Disallow: /

但要注意一个现实:不想被训练和想被 AI 搜索引用是两件事。如果你希望自己的内容出现在 ChatGPT、Perplexity 的回答里,那允许抓取是前提。这个取舍没有标准答案,但一定要"有意识地选",而不是复制别人模板时无意中拦掉了。

三、用 Nginx 日志验证 robots.txt 是否真的生效

写完规则不看效果,是最大的浪费。验证方法和上一节的开通日志同理,从 access.log 里直接看爬虫行为。

第一步:看爬虫读 robots.txt 的状态码

grep -i 'robots.txt' /var/log/nginx/access.log | awk '{print $9, $1, $NF}' | sort | uniq -c | sort -rn | head -20

必须全部是 200。 如果出现 403、404 或 500,那再完美的规则也等于零——爬虫拿不到规则,就默认"什么都能抓"。

常见的 404 原因:WordPress/Typecho 的重写规则把请求劫持了、做了 CDN 但没把 robots.txt 加入缓存白名单、或者用了 WAF 拦掉了非浏览器 UA。这三条都要排查。

第二步:确认 Disallow 目录真的没被抓

# 统计爬虫对 admin/action/search 的抓取次数
grep -iE 'googlebot|bingbot|baiduspider' /var/log/nginx/access.log \
  | grep -E '/admin/|/action/|/search/' \
  | awk '{print $1, $7}' | sort | uniq -c | sort -rn | head

如果这里出现了大量记录且状态码是 200,说明有爬虫无视了你的规则,或者你漏掉了某个 UA。这时候上 Nginx 层面做硬拦截:

# /etc/nginx/conf.d/bot-block.conf
map $http_user_agent $bad_bot {
    default 0;
    ~*Bytespider        1;
    ~*SemrushBot        1;
    ~*AhrefsBot         1;
    ~*MJ12bot           1;
}

server {
    if ($bad_bot) { return 403; }
}

注意 map 必须写在 http 块里,ifserver 块里 return 是少数被官方认可的 if 用法之一。不要用 if 去包 proxy_pass,那是著名的灾难源头。

第三步:统计爬虫抓取预算被谁吃了

这是诊断"收录慢"最直接的一招:

grep -i 'googlebot' /var/log/nginx/access.log \
  | awk '{print $7}' | sed 's/[0-9]\+/N/g' | sort | uniq -c | sort -rn | head -25

把 URL 里的数字替换成 N 做聚合,能看出爬虫的抓取预算花在哪个 URL 模式上。如果发现大量预算花在 /tag//page/N?replyTo=N 这类低价值页面,说明抓取预算被浪费了——这些正是该在 robots.txt 里 Disallow 或者加 noindex 的。反之如果预算集中在你的核心文章页,说明 robots.txt 起到作用了。

这个分析可以做得很细,比如只统计正文页的抓取频率,看新文章多久被爬一次。

四、sitemap.xml:不是生成完就完事

sitemap 的作用是告诉搜索引擎"这些 URL 存在,请来抓",尤其对新站和内链较浅的页面价值巨大。但它也有限制,很多人不知道:

  • 单个 sitemap 文件最多 50,000 个 URL,且未压缩不超过 50MB。超了要拆分成 sitemap index。
  • <lastmod> 只有在你诚实填写时才有用。如果每次生成都写当前时间,Google 会学会忽略你的 lastmod,之后你的真实更新也失去优先级。这是最容易被自己搞坏的一个字段。
  • sitemap 里的 URL 必须与该页面的 canonical 完全一致。如果 sitemap 写 http 而页面 canonical 是 https,或者 sitemap 带 www 而站内链接不带,这些 URL 会被判为"替代页面",白白浪费。

一个可靠的 lastmod 生成方式

用文件真实修改时间来填,而不是生成时间:

<?php
// 生成 sitemap 片段,lastmod 取文章真实更新时间
foreach ($posts as $p) {
    printf(
        "<url><loc>%s</loc><lastmod>%s</lastmod></url>\n",
        htmlspecialchars($p['permalink'], ENT_QUOTES, 'UTF-8'),
        date('c', $p['updated'])   // 注意是 updated 而非当前时间
    );
}

如果内容站的数据库里有 modified 字段,就用它。没有的话建议加上——它对 sitemap 的准确性影响很大。

拆分与索引

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap><loc>https://www.example.com/sitemap-posts-1.xml</loc>
    <lastmod>2026-09-01</lastmod></sitemap>
  <sitemap><loc>https://www.example.com/sitemap-posts-2.xml</loc>
    <lastmod>2026-09-20</lastmod></sitemap>
  <sitemap><loc>https://www.example.com/sitemap-tags.xml</loc></sitemap>
</sitemapindex>

拆分的好处不只是 50,000 上限:爬虫可以只重新抓取 lastmod 变化的那个分片,整体带宽和时间都省。对每天更新多篇的站点,按月份或按分类分片是很好的策略。

五、robots.txt 与 sitemap 的交叉陷阱

这两者一起用时,下面这些矛盾必须避免,每一条我都在真实站点上见过:

  1. sitemap 里列了被 Disallow 的 URL。爬虫拿到 sitemap 来抓,被 robots 拦住,结果是一条"已发现但被 robots.txt 阻止"的提示,纯浪费。生成 sitemap 时要用同一套规则过滤。
  2. Disallow: / 或者 Disallow: /* 这种全局禁抓,然后又期待站点被收录。这几乎等于向搜索引擎申请"请删除我的站"。生产环境绝对不要出现。
  3. 把 sitemap 放在被 Disallow 的目录下,比如 Disallow 了 /assets/ 而 sitemap 在 /assets/sitemap.xml。爬虫连地图都拿不到。
  4. 多域名/多协议混写。sitemap 里同时出现 http 和 https、带 www 和不带 www,会被判为跨域名指引,效果打折。只写一种,就是你的 canonical 形式。
  5. robots.txt 本身被缓存太久。CDN 上把 robots.txt 的缓存设成 7 天,你改了规则但搜索引擎拿到的还是旧的。建议 robots.txt 的缓存时间控制在 1 小时以内,并在 CDN 上加一条强制覆盖规则。

六、上线后的验证清单

  1. curl -I https://你的域名/robots.txt,确认返回 200Content-Type: text/plain,且没有意外跳转到 404 页。
  2. curl -s https://你的域名/robots.txt 肉眼核对语法,特别检查是否有中文标点(全角冒号、全角引号)——这类字符爬虫会直接忽略整行,是极隐蔽的坑。
  3. curl -I https://你的域名/sitemap.xml,同样确认 200 和 application/xml
  4. Search Console 的 robots.txt 测试工具 + 已编入索引页面报告,对比改动前后两周的数据。
  5. 用 access.log 的三条 awk 命令验证爬虫实际行为,而不是"假设"。
  6. 两到四周后复查:抓取预算分布是否向你期望的 URL 模式倾斜。

robots.txt 和 sitemap 是低成本高杠杆的两件事:写好一次,长期生效;写错一次,静默掉收录。绝大多数"收录变慢""新文章不收录"的问题,根源都能在这两个文件加一条日志分析里找到答案。先验证,再优化,顺序别搞反。

Last modification:September 21st, 2026 at 08:24 pm

Leave a Comment