KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

02 · 抓取:爬虫愿意花多少资源抓你的站 — keel 龙骨

本章目标:讲清「抓取(Crawling)」环节的规则——爬虫以谁的身份来、受什么约束、状态码如何被解读。 验收标准:能写出正确的 robots.txt,并对每个状态码说清它会让爬虫做什么。

本章目标:讲清「抓取(Crawling)」环节的规则——爬虫以谁的身份来、受什么约束、状态码如何被解读。
验收标准:能写出正确的 robots.txt,并对每个状态码说清它会让爬虫做什么。

为什么要单独讲抓取:发现只解决"知道 URL 存在",抓取才解决"真的把内容取回去"。这个环节有两个独立变量——能不能抓(robots 规则)与愿不愿意多抓(抓取预算)。前者是准入,后者是效率。


一、爬虫的身份:User-Agent

爬虫每次请求都会带上 User-Agent(UA),这是你识别它、也约束它的唯一依据。

爬虫 UA 关键字 归属 执行 JS
Googlebot Googlebot(Smartphone / Desktop) Google 会(二次渲染,见第 03 章)
Bingbot bingbot Microsoft 有限
Baiduspider Baiduspider 百度 基本不执行
Sogou Sogou web spider 搜狗 基本不执行
Bytespider Bytespider 字节 基本不执行
GPTBot GPTBot OpenAI 训练/索引 不执行
OAI-SearchBot OAI-SearchBot OpenAI 搜索 不执行
ClaudeBot ClaudeBot Anthropic 不执行
PerplexityBot PerplexityBot Perplexity 不执行

重要事实:UA 是可以伪造的。 日志里出现自称 Googlebot 的 IP,不代表真是 Google。验证方法是双向 DNS(反解到 *.googlebot.com 再正解回同一 IP),第 05 章细讲。本章只需记住:robots.txt 的规则按 UA 匹配,这是"协约"而非"强制"。

# 用真实 Googlebot UA 请求你的页面(模拟爬虫视角)
curl -s -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
  https://www.xxxxxx.cn/learning/ -o /dev/null -w "%{http_code}\n"

二、robots.txt 的语义

robots.txt 放在域名根目录(/robots.txt,不是 /learning/robots.txt)。它由一组 User-agent 段组成,每段下是 Allow / Disallow 规则。

User-agent: *
Allow: /
Disallow: /admin/
Disallow: /api/

User-agent: GPTBot
Allow: /
Disallow: /admin/

Sitemap: https://www.xxxxxx.cn/sitemap.xml

语义要点

概念 规则 常见误解
匹配顺序 最长匹配优先,不是先写优先 以为写在前面的规则说了算
* 通配 匹配任意字符,如 Disallow: /*.pdf$ 以为只在路径末尾生效
$ 结尾锚 Disallow: /*?$ 屏蔽带空查询串的 URL 漏写导致参数页被大量抓取
空 Disallow Disallow: 等于全部允许 与 Disallow: /(全禁)混淆
多个 UA 段 只取最匹配的那一段,不合并 以为规则会叠加
抓取延迟 Crawl-delay 仅部分爬虫(如 Bing)支持 以为 Google 也遵守

两个致命的默认值:

  1. 多个脚手架会生成 Disallow: / 的全站禁止 robots.txt,上线时忘了改——这是"其他都对但死活不收录"的经典原因。
  2. robots.txt 返回 5xx 时,规范的爬虫会保守地停止抓取全站;返回 404 则视为"无限制,随便抓"。所以 robots.txt 必须稳定返回 200。
curl -s -o /dev/null -w "%{http_code}\n" https://www.xxxxxx.cn/robots.txt   # 必须 200
curl -s https://www.xxxxxx.cn/robots.txt | grep -i disallow

注意:robots.txt 只是"君子协定",不是访问控制。真要阻止访问请用登录鉴权或 noindex。


三、抓取预算:Crawl Budget 是什么

抓取预算是搜索引擎给每个站点分配的有限抓取配额。官方把它拆成两个变量:

结论:预算 = min(速率上限, 抓取需求)。 小站通常受限于"需求"而非"速率";大站(十万 URL 级)才常撞上速率上限。

什么行为在浪费预算

浪费行为 具体表现 治理
无限参数空间 ?sort=、?page=、?sessionid= 组合爆炸 robots 屏蔽 或 canonical 归一
重定向链 http → https → www 两跳 sitemap/内链直接指向最终 URL
大量 404/410 已删除页面仍在 sitemap 或内链里 及时清理
重复内容 同一内容多个 URL canonical 声明
慢响应 爬虫等超时,配额被空耗 优化 TTFB
大量低价值页 空标签页、分页深链 noindex 或 robots 屏蔽
# 统计服务端看到的抓取量(详见既有《SEO 收录与优化》第 05 章)
grep -icE 'googlebot' /var/log/nginx/learning.access.log

给中小站的结论:几百到几千页的站点,预算基本不是瓶颈。但"让 sitemap 全部指向 200 的 canonical URL"是任何规模都该守的底线。


四、HTTP 状态码对爬虫的语义

这是抓取环节最容易被忽视、后果最直接的一张表:

状态码 爬虫的理解 正确用法 误用后果
200 正常内容,可索引 正常页面 —
301 永久搬家,权重传给新址 迁移、规范化 链太长浪费预算
302 临时搬家,不传权重 短期重定向 误当 301 用,权重丢失
304 内容未变,用缓存 配合 ETag/Last-Modified 能省带宽与预算
404 不存在,逐渐从索引移除 已删除页面 sitemap 里混入会降可信度
410 永久删除,更快移除 确定不再有的页面 —
429 请求过多,请退避 限流时告知爬虫 持续 429 会让抓取降速
503 服务暂不可用,会稍后重试 维护窗口 返回 200 的"维护页"是软 404
5xx 服务故障,降低抓取 仅真故障时 频繁 5xx 会砍预算

两个必须记住的语义:

  1. 302 不传权重,301 传。这是迁移时最容易踩的坑。
  2. "未登录"必须返回 401/403,不能用 200 返回登录页——否则爬虫会把登录页当成该 URL 的正文收录。SPA 尤其容易犯:所有未知路由回退 index.html 且返回 200,产生软 404。
curl -s -o /dev/null -w "%{http_code}\n" https://www.xxxxxx.cn/learning/not-a-real-page/  # 期望 404

五、canonical 与重复内容

同一个内容可能有多个 URL:带尾斜杠/不带、http/https、裸域名/带 www、带跟踪参数。爬虫会认为它们是不同页面。

<link rel="canonical" href="https://www.xxxxxx.cn/learning/courses/tools/lessons/1/">

规则:

  1. 必须自引用:页面 A 的 canonical 指向自己,而不是首页。
  2. 与 sitemap、内链三者一致——三处不一致时,搜索引擎会自己猜,结果不可控。
  3. canonical 是"建议"不是"命令":它无法跨域强制生效,跨域时还需配合 301 与 sitemap。
curl -s https://www.xxxxxx.cn/learning/courses/tools/lessons/1/ \
  | grep -oE '<link rel="canonical"[^>]*>'

六、故障速查

现象 抓取层原因 处理
全站都不收录 robots.txt 是 Disallow: / 改成 Allow
只抓到首页就停了 robots 误屏蔽了深层目录 检查规则最长匹配
大量 301 被频繁抓取 sitemap/内链用了非 canonical URL 全部指向最终 URL
服务器带宽被爬虫跑满 无速率限制/无缓存 用 304、限流,观察是否真爬虫
参数页被海量抓取 无限 URL 空间 robots 屏蔽 + canonical
搜索结果里出现登录页 未登录返回了 200 改为 401/403

七、本章验收

# 三连检:robots 可访问、未知路径 404、canonical 自引用
curl -s -o /dev/null -w "robots=%{http_code}\n" https://www.xxxxxx.cn/robots.txt
curl -s -o /dev/null -w "404test=%{http_code}\n" https://www.xxxxxx.cn/learning/nope/
curl -s https://www.xxxxxx.cn/learning/ | grep -oE 'rel="canonical"[^>]*'

抓取解决的是"HTML 拿到了"。但拿到的 HTML 里有没有正文,取决于下一章的渲染环节。

进入 keel 阅读