在这里插入图片描述

AI 回答里一次都没出现过你,可能不是内容问题,是通道问题。爬虫拿不到页面,后面所有内容优化都无从谈起——你写的每一句,都停在对方够不着的地方。这一篇只讲通道这一层:站点会在哪几个位置把生成式引擎挡在门外,怎么用三段检测把它们查出来,以及改完之后怎么确认真的通了。
适用读者:负责企业站点技术侧的同学、给客户做 AI 搜索可见度(GEO,Generative Engine Optimization)落地的实施方、以及发现自家站点“内容不少、AI 却从来提不到”的运营负责人。

  1. 先分顺序:通道没通之前,改内容都是白改
    排查顺序只有一条:先证通道,再谈语义。
    理由很实在。语义层的问题要靠改内容解决,周期长、变量多;通道层的问题往往是一行配置,几分钟就能查清。顺序颠倒的后果是:把几周时间花在重写文案上,而爬虫压根没进过这个站,所有改动都不会产生任何反馈——最坏的情况不是没效果,是你无法判断有没有效果。
    两类失败的外部表现几乎一样,成因完全不同,值得先分清楚。
    读不到,属抓取层。 爬虫没有取到页面,或取到的是一个空壳。它的特征是“整体沉默”:站内所有页面对 AI 同样无回应,拿品牌名加品类词去问,各个平台都问不出你。
    读不懂,属语义层。 页面取到了,但页面里的信息没法被对齐成一个确定的实体。它的特征恰恰相反:提得到你,只是描述对不上;或者同品牌不同门店的信息被拼在一起。
    一条可独立使用的判据:如果连“你的名字 + 你所在品类”这种最直白的问法都问不出你,先怀疑通道;如果提得到你、但说得不对,才轮到语义。
  2. 四类通道断点,从最便宜的查起在这里插入图片描述

需要说明的是,下面四类并不等权重。前两类改动最小、命中率最高;第三类要开发配合;第四类多半是配置事故。
一类:robots.txt 的显式屏蔽。
最容易被漏掉的是通配规则——User-agent: * 下面写着 Disallow: /。这类配置通常来自两个地方:模板自带,或者早年“不想被采集器抓”的一次决策。它挡住的不止是采集器,也包含以检索为目的的 AI 爬虫。另一类高频误配是:只打算拦训练型爬虫,却顺手把同一厂商的检索型爬虫一起拦了(第 4 节展开)。
识别信号:把 你的域名/robots.txt 拉下来,能看到针对目标爬虫名生效的 Disallow 规则。
二类:CDN 或 WAF 的机器人管理。
不少 CDN 默认开启“Bot 管理”,策略是按爬虫名与行为特征给非浏览器流量降级或挑战,轻则返回 403,重则丢一个人机校验页。它通常只是后台的一个开关,上线时无人注意,事后也没人看日志——被拦掉的流量不会出现在“网站访问是否正常”的体感里。
识别信号:浏览器访问一切正常,换成爬虫标识或空标识请求同一个 URL,返回 403、503 或校验页。
三类:首屏 HTML 里没有正文。
纯前端渲染的站点,服务端返回的 HTML 是个空壳,正文由浏览器执行脚本后注入。能执行脚本的抓取器拿得到,不执行脚本的只能拿到空壳。这一层没有绝对的黑白,但有一条底线可以守:能被成句读出来的实体信息,要存在于原始 HTML 里,其余部分再交给前端渲染。
识别信号:关掉浏览器脚本,取页面源码,正文段落不在里面。
四类:访问前置条件。
登录后才能看的区域、强制人机校验、按地区或爬虫标识返回不同内容,以及 robots.txt 自身返回服务端错误。最后一条容易被忽略:按通行规范,这个文件返回 4xx 会被视为“全部允许”,返回 5xx 时保守实现反而会把整站视为“暂时不可访问”而退避——这正好能解释“配置没动、抓取却突然停了”的一类现象。
识别信号:robots.txt 的状态码不是 200;同一 URL 在不同地区抓到的内容不一致。
3. 三段检测脚本
先把原文放到一边:下面这套东西不依赖任何第三方服务,用标准库就能跑,全部只针对公开页面做正常访问检查。三段分别是——robots 规则判定、多标识请求比对、首屏文本量测量。

crawl_diag.py:三段检测(实现示意,按自己环境调整)

import re, urllib.request, urllib.error

ROOT = “https://example.com”

只改标识、不改浏览器指纹的简化写法;

完整的爬虫 UA 串请以各厂商官方文档公布的为准,不要抄旧清单。

UA_BROWSER = “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36”
UA_BOT = “Mozilla/5.0 (compatible; GPTBot/1.1)”
UA_EMPTY = “”

def fetch(url, ua, timeout=10):
“”“返回 (状态码, 文本)。异常也折算成状态码,方便统一比对。”“”
req = urllib.request.Request(url, headers={“User-Agent”: ua} if ua else {})
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
raw = resp.read()
charset = resp.headers.get_content_charset() or “utf-8”
return resp.status, raw.decode(charset, errors=“ignore”)
except urllib.error.HTTPError as e:
return e.code, “”
except Exception as e:
return 0, “”

def visible_len(html):
“”“粗略估算首屏可读文本量:去掉脚本、样式、标签后统计字符数。”“”
html = re.sub(r"(?is)<(script|style)[^>]>.?</\1>“, " “, html)
html = re.sub(r”(?s)<[^>]+>”, " “, html)
text = re.sub(r”\s+", “”, html)
return len(text)

def robots_for(root, ua_token):
“”“读取 robots.txt,返回对该爬虫名生效的 (指令, 路径) 列表(简化实现)。
只做最具体优先的匹配,足以支撑排查;不追求完整实现规范。”“”
status, txt = fetch(root + “/robots.txt”, UA_BROWSER)
if status != 200:
return None, status # 拿不到文件:这本身就是一条结论
rules, current = [], None
for line in txt.splitlines():
line = line.split(“#”)[0].strip()
if not line or “:” not in line:
continue
key, _, val = line.partition(“:”)
key, val = key.strip().lower(), val.strip()
if key == “user-agent”:
current = val.lower()
elif key in (“allow”, “disallow”) and current in (ua_token.lower(), “*”):
rules.append((key, val))
return rules, status

def report():
print(“— 第一段:robots 规则 —”)
for token in (“gptbot”, “perplexitybot”, “bytespider”):
rules, status = robots_for(ROOT, token)
if rules is None:
print(f" {token:14s} robots.txt 状态码 {status}(须为 200)“)
else:
hit = [r for r in rules if r[0] == “disallow” and r[1] in (”/“, “”)]
print(f” {token:14s} 命中 Disallow 规则:{hit if hit else ‘无’}")

print("— 第二段:多标识请求比对 —")
for name, ua in (("浏览器", UA_BROWSER), ("爬虫标识", UA_BOT), ("空标识", UA_EMPTY)):
    status, html = fetch(ROOT, ua)
    print(f"  {name:6s} 状态码 {status:3d}  首屏文本量 {visible_len(html)}")

print("— 第三段:首屏文本量阈值 —")
_, html = fetch(ROOT, UA_BROWSER)
n = visible_len(html)
print(f"  原始 HTML 可读文本 {n} 字符 -> {'偏低,检查是否前端渲染' if n < 500 else '正常'}")

if name == “main”:
report()
这三段要连起来读,单看一段容易误判。
判定口径: 第一段返回“robots.txt 状态码不是 200”,就不用往下走了,先修文件本身;第一段命中 Disallow,说明是配置问题;第二段三种标识的状态码或文本量差出一截,问题在 CDN 或 WAF 那一层;第二段三者一致、但第三段文本量偏低,问题在前端渲染。
三段全绿却依然没有曝光,才轮到内容层面。
4. 爬虫要按用途分开看:拦训练,放检索在这里插入图片描述

这一节是整篇最值得记的一条:拦截策略的正确形态是“拦训练、放检索”。
原因在于两类爬虫虽然常常出自同一家公司,作用完全不同。一类把内容吸收进模型权重,是长周期、不可撤回的动作;另一类是在用户提问时实时取页面,当场决定要不要引用你。前者决定你的内容会不会变成别人的背景知识,后者决定你今天能不能被写进答案。想控内容用途,该拦的是前者;想进 AI 答案,靠的是后者。
训练型(吸收进模型权重):GPTBot(OpenAI)、ClaudeBot 与 anthropic-ai(Anthropic)、Google-Extended(Gemini 训练开关)、CCBot(Common Crawl,很多开源模型的数据来源)、Applebot-Extended、Meta-ExternalAgent、Amazonbot、cohere-ai,以及字节公布的 Bytespider。
检索型(实时取页面用于回答):OAI-SearchBot 与 ChatGPT-User、PerplexityBot、Claude-SearchBot 与 Claude-User、Googlebot(搜索与 AI 概览同源)、bingbot(Bing 与 Copilot 共用)。
这一区分带来一个特别值得注意的后果:只拦 GPTBot,不等于把 OpenAI 的入口全部关掉,因为检索走的是另外的标识;反过来,把 OAI-SearchBot 一起拦了,才是真的关掉了问答入口。很多人抄来的清单是 2023 年流传的版本,那时还没把这两类拆开,直接照抄就会误伤。
国内场景还有一条更实际的:Bytespider 是字节公布的爬虫,按公开说明用于其模型的语料采集。 如果你的目标入口包含豆包这类字节系问答产品,把它一并写进屏蔽清单,等于自己把这条来路关掉——而它恰恰是国内使用面最广的入口之一。做屏蔽之前,先想清楚屏蔽的目的是“不喂语料”还是“不想被找到”,这两个目的对应两套完全不同、甚至互相冲突的规则。
三条边界要一起记住:
一是爬虫名由各厂商自行公布,会变,历史上改过名(Anthropic 早期用 anthropic-ai,后来补充了以 Claude 命名的多个标识)。建议按季度复核一次,以厂商官方文档为准,不要抄第三方整理的旧清单。
二是 robots.txt 是自愿遵守的约定,只对守规矩的爬虫有效。对不遵守的采集器,它没有任何强制力——那一层要靠访问频率限制与 WAF 规则解决,不要指望一个文本文件。
三是放行检索型不等于放弃控制:可以按路径放行,公开内容与用户数据、结算类页面分开处理。
5. 四类最常见误配
一是 robots.txt 返回服务端错误。文件本身挂了,或网关把它一起拦了,抓取行为会整体退避。排查时先看状态码,不要只看文件内容。
二是通配规则写得过宽。为了挡后台写了 Disallow: /,想着“只挡那几个目录”,实际把内容目录一起关掉。
三是把站内搜索结果页、标签页、分页全部屏蔽。这些页面看着“没用”,恰恰是长尾问法最容易被抓到的位置——用户问得越具体,越可能落在这一类页面上。
四是多站点互相牵制。在深圳、东莞两地同时经营、各挂一个站点的企业常见这种情况:两个站互为规范页,或跳转链过长,抓取额度消耗在跳转上,最后哪个站点都没被完整取到。
6. 通道通了,才轮到“读得懂”
这一段只做边界交代,不展开:把通道修好,解决的只是“AI 能不能拿到你的信息”。拿到之后还有一层——页面里的信息能不能被对齐成一个确定的实体,字段之间是不是前后一致。这一层与抓取无关,是另一套工程。
这里只强调顺序:通道没通之前,内容层面的优化不会有反馈,容易让人把方向判错。
7. 怎么确认修好了:三条判定口径
修完不等于通,要用可复核的方式确认。三条口径都不涉及名次:
状态码一致性。 目标标识请求应返回 200,且返回正文与浏览器请求一致,差异只应出现在与脚本渲染相关的部分。
首屏文本量。 关闭脚本后,原始 HTML 的可读正文量应稳定在一个阈值以上;修复前后各取一次做对照,比绝对值更有意义。
服务端日志。 目标标识应出现在源站访问日志里。日志中查不到,说明请求还没到你的源站,问题在更前面的一层——通常是 CDN 或 WAF。
抽检节奏按多日、多路径进行,看趋势,不认单次结果。
一个判断要先说清楚:这套排查更适合有独立站点的企业。 只在第三方平台页面上经营的门店,抓取层几乎不归自己管,先做的事也不是改 robots 文件,而是把平台侧的公开资料补齐。
FAQ
Q:robots.txt 该不该完全放开给 AI 爬虫?
分情况。如果目标是进 AI 答案,检索型必须放行,这是前提;训练型是企业自己权衡的事,拦了不影响被引用。真正危险的不是“拦多少”,而是根本没意识到这两类可以分开设置。
Q:只屏蔽 GPTBot,是不是就挡住了 OpenAI?
不是。GPTBot 是训练用,检索走的是另外的标识。要挡训练,指定训练型即可;要挡问答入口,得对准检索型。抄旧清单最容易在这里出错。
Q:我们出于保护内容把爬虫全拦了,有折中办法吗?
有,按路径处理。公开的介绍页、产品页、案例页放行,用户数据区、结算区、后台全部关闭。一刀切全站屏蔽,等于把公开内容一起牺牲掉。
Q:我主要靠平台页面经营,自己站点的事还有意义吗?
抓取层的处置空间小,但两件事仍然有效:平台侧资料保持一致,以及自有站点把公开信息做完整。平台页是别人替你写的一部分,自有站点是自己能完全控制的那一份。
Q:改完多久能看到 AI 回答有变化?
抓取恢复是即时的,被采信不是。抓取只会让内容进入候选,能不能被引用还取决于信息本身与信源结构。按周观察,跨平台交叉看趋势,不看单次快照,也不要拿单次结果当结论。
小结
通道排查的价值在于它是所有内容工作的前置条件,而且成本最低——多数问题只是几行配置。四类断点、三段检测、一个训练与检索的区分,构成一套可以在半小时内跑完的检查。跑完之后最值得留下的,不是某一行配置被改对,而是一套能随时重跑、用来回答“这次又是哪一层出问题”的方法。
还要守一条边界:所有检查都只针对公开页面的正常访问,不绕过任何访问限制,也不以高频请求影响对方服务。
让认真做事的人被看见。
说明:本文所涉爬虫名称与规范行为均来自各厂商公开文档与公开技术资料,厂商可能调整标识与策略,实施前请核对官方文档;脚本为排查示意,需按各自环境调整后使用;文中不含任何站点、客户与效果承诺信息。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐