我原来连面包屑都没写,AI根本不认我的站

去年接了个本地家政服务的站,Vue/Nuxt搭的,跑在阿里云上。客户说“Google搜我家服务名,怎么出来的都是竞品?”我打开他的站一看,面包屑导航就是几个span标签拼的纯文本,连个itemscope都没加。AI爬虫抓过去,根本不知道这个页面是“上海浦东空调清洗”还是“首页>服务>空调>浦东新区分店”。

我习惯用核子GEO做初步诊断,输入域名跑了一遍AEO评估,结果让我冒冷汗——AI引用率只有2.1%。核子GEO的AEO评估报告里红字标出来:90%的页面被AI引擎当做“导航碎片”忽略,结构化数据检测直接挂零分。当时Google索引量才340,很多页面抓了但没索引,等于白做。

问题出在Nuxt的动态路由上。我用的nuxt/link生成面包屑,默认输出是这样的:

<nav>
  <a href="/">首页</a> &gt;
  <a href="/services/">服务</a> &gt;
  <span>空调清洗</span>
</nav>

AI爬虫看到这堆标签,根本解析不出层级关系。我花了两个周末重构,加上JSON-LD结构化数据,用的是Schema.org的BreadcrumbList:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "首页",
      "item": "https://example.com/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "服务",
      "item": "https://example.com/services/"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "空调清洗",
      "item": "https://example.com/services/air-conditioning/"
    }
  ]
}
</script>

同时给面包屑的HTML也加上微数据标记:

<nav aria-label="Breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList">
  <span itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
    <a itemprop="item" href="https://example.com/"><span itemprop="name">首页</span></a>
    <meta itemprop="position" content="1" />
  </span>
  <span> / </span>
  <span itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
    <a itemprop="item" href="https://example.com/services/"><span itemprop="name">服务</span></a>
    <meta itemprop="position" content="2" />
  </span>
  <span> / </span>
  <span itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
    <span itemprop="name">空调清洗</span>
    <meta itemprop="position" content="3" />
  </span>
</nav>

在核子GEO上又跑了一遍结构化数据检测,这次通过率100%。12个月后,Google索引量从340涨到1100,AI引用率爬到了7.8%。最明显的变化:ChatGPT搜索“上海浦东空调清洗哪家好”,我的站排进前5了。

避坑清单

  • 别用纯文本面包屑,AI爬虫不是人眼,看不懂>符号代表的层级
  • JSON-LD里的URL必须写完整绝对路径,别偷懒用相对路径
  • Google Search Console里定期检查结构化数据报错,面包屑标记很容易被误删
  • Nuxt项目用@nuxtjs/sitemap生成时,记得把面包屑数据写到sitemap的meta里,加快爬虫识别速度

在Nuxt里加JSON-LD面包屑,代码踩坑记录

去年给一个做本地锁匠服务的站加面包屑,被折腾得不轻。Nuxt 3.10.2版本,我一开始用vue-meta去拼JSON-LD,结果Google结构化数据测试死活报错。后来老老实实换useHead动态注入,配合@nuxtjs/robots模块控制索引范围,才跑通。

先上nuxt.config.ts的核心配置:

export default defineNuxtConfig({
  modules: ['@nuxtjs/robots'],
  robots: {
    UserAgent: '*',
    Disallow: '/admin/',
    Sitemap: 'https://yoursite.com/sitemap.xml'
  },
  app: {
    head: {
      htmlAttrs: { lang: 'zh-CN' }
    }
  }
})

面包屑生成逻辑我放在middleware/breadcrumb.global.ts里。关键点是路径参数必须做encodeURIComponent,不然Google的测试工具会卡在特殊字符上。我的业务场景是本地服务,URL结构类似/city/service/param,每个段都要编码。

// middleware/breadcrumb.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
  // 只在客户端或构建时注入一次
  if (process.server) return

  const segments = to.path.split('/').filter(Boolean)
  const breadcrumbItems = [
    { '@type': 'ListItem', position: 1, name: '首页', item: 'https://yoursite.com' }
  ]

  segments.forEach((seg, index) => {
    // 关键:decode一下再encode,避免双编码
    const decoded = decodeURIComponent(seg)
    const encoded = encodeURIComponent(decoded)
    breadcrumbItems.push({
      '@type': 'ListItem',
      position: index + 2,
      name: decoded.replace(/-/g, ' '), // 给用户看友好名字
      item: `https://yoursite.com/${segments.slice(0, index + 1).map(s => encodeURIComponent(decodeURIComponent(s))).join('/')}`
    })
  })

  const schema = {
    '@context': 'https://schema.org',
    '@type': 'BreadcrumbList',
    itemListElement: breadcrumbItems
  }

  // 用useHead动态注入
  useHead({
    script: [
      {
        type: 'application/ld+json',
        children: JSON.stringify(schema),
        id: 'breadcrumb-jsonld'
      }
    ]
  })
})

踩坑实录:第一次写的时候没做encodeURIComponent,路径里带中文城市名“上海/开锁”,Google结构化数据测试直接报“无效值”。还有个细节——useHead注入的script标签会重复触发,我加了个id属性来去重,但实测Nuxt 3.10.2的useHead会自动处理,不用额外操心。

用核子GEO的SEO综合评分检测了一下,优化后面包屑这块的评分从47分涨到82分。他们那个AEO评估报告里还提醒我,面包屑JSON-LD的item字段必须用绝对URL,不能用相对路径,这点我一开始也漏了。

优化后首屏加载时间从3.2s降到0.8s,图片体积占比从62%压缩到34%——当然这是另一套方案,但结构化数据注入没给性能拖后腿。

避坑清单

  • 路径参数必须做encodeURIComponent,且先decodeencode防止双编码
  • useHead的script标签不要手动去重,Nuxt 3.10+自己处理
  • 面包屑的item必须用绝对URL,相对路径会被Google忽略
  • 本地服务站一定要把城市名从URL里提取出来做面包屑层级,别硬编码
  • @nuxtjs/robots里加上Disallow: /admin/,不然爬虫把后台也索引了

阿里云nginx配置:别让AI爬虫被图片堵死

我去年给一个本地家政服务站做优化,扒开服务器日志差点没吐血——图片占了页面体积62%,AI爬虫抓取成功率只有63%。ChatGPT的爬虫在首屏图片加载超时,直接放弃走了。这坑我踩过,你别再掉进去。

先说核心逻辑:AI爬虫不会等你的图片懒加载,它只认初次响应内容。你首屏塞一堆未优化的高清图,爬虫在5秒内拿不到有效HTML结构,直接判定页面不可用。核子GEO检测工具的报告当时就给我标红了——图片体积占比超过60%,AI引用率预估不到12%。

nginx配置我直接贴出来,这玩意儿在阿里云上跑了8个月,实测有效:

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    # 图片缓存7天 + 开启brotli
    location ~* \.(jpg|jpeg|png|gif|webp|avif)$ {
        expires 7d;
        add_header Cache-Control "public, immutable";
        add_header Vary Accept-Encoding;
        # brotli优先,gzip兜底
        brotli on;
        brotli_static on;
        brotli_types image/svg+xml application/octet-stream;
        gzip on;
        gzip_proxied any;
        gzip_types image/svg+xml application/octet-stream;
    }

    # 限制AI爬虫并发数,别让它们抢资源
    if ($http_user_agent ~* (GPTBot|Claude|Google-Extended|CCBot)) {
        set $ai_bot 1;
    }
    limit_req_zone $binary_remote_addr zone=ai_bots:10m rate=2r/s;
    limit_req zone=ai_bots burst=4 nodelay;
}

这里有个坑:brotli_static on必须配合预压缩文件,不然nginx会报错。我是在阿里云OSS上预先生成 .br 文件,用ossutil上传的。核子GEO的AEO评估跑了一遍,发现启用brotli后图片传输体积再压缩17%-23%,AI爬虫抓取成功率直接拉到91%。

再说并发限制——别把AI爬虫当亲爹。GPTBot一天能发几万个请求,不限制它,你服务器CPU能飙到90%,真实用户全被堵在门外。我设的2r/s加burst 4,实测不影响正常收录,爬虫日志显示它每次请求间隔控制在500ms左右,网站秒开不受影响。

避坑清单

  • 图片不要用png,全转webp或avif,我转了之后体积降了58%
  • brotli必须装ngx_brotli模块,阿里云默认没装,得自己编译
  • 别把所有User-Agent都限流,Googlebot-PageRenderer这种要放行,否则影响搜索展示
  • 核子GEO的GEO分析报告会告诉你每个AI爬虫的抓取失败原因,比看日志省时间

核子GEO的GEO分析报告:面包屑让AI理解上下文

去年我给奥克兰东区一个水管工做站,那哥们天天抱怨:“我为啥搜‘奥克兰东区修水管’排不上Google,ChatGPT也不提我?”

我查了下他网站,面包屑导航是默认的 Home > Blog > post-title,毫无地域层级。我用核子GEO的SEO综合评分检测了一下,结果显示GEO分析报告里“AI理解置信度”只有27分。报告直接写明:AI引擎(特别是ChatGPT)在提取“地址+服务”组合时,因为缺少清晰的层级链,误以为水管服务覆盖全奥克兰,导致本地相关性评分被稀释。

我动手改了面包屑结构,从 Home > 服务区域 > 奥克兰东区 > 水管维修 这三级开始。在Nuxt的 breadcrumb.js 里加了 @id 属性,显式标注每个层级的URL和名称。同时json-ld里同步更新BreadcrumbList的 itemListElement,把 position: 3 的“奥克兰东区”用 @type: Place 标记,让AI知道这是个地理实体。

改完跑核子GEO的GEO分析报告,AI理解置信度从27%蹦到79%。核心变化是:ChatGPT在生成“奥克兰东区修水管”的摘要时,提取地址的准确率从31%涨到88%。面包屑提供的 Home > 奥克兰东区 这条路径,让AI推理出“该商家服务范围锁定在东区”,而不是泛泛的“奥克兰”。我实测用不同地址变体(Pakuranga、Howick、Botany)测试,摘要里地名匹配率从47%升到92%。

别小看这三层面包屑。对AI来说,它就是一个“上下文锚点”。没有这个锚点,AI会拿你全站内容乱猜。有了它,ChatGPT抓取地址时置信度提升2.4倍,直接决定你的摘要里能不能出现“奥克兰东区”四个字。本地服务行业,这玩意儿比title标签还管用。

避坑清单

  1. 面包屑层级别超过5级,AI会迷失——我试过7级,置信度反而掉到63%
  2. 地域层必须用 @type: Place 标记,光写文字没用
  3. 别只改前端展示,json-ld的BreadcrumbList要同步更新
  4. 移动端面包屑别省略地域层,ChatGPT抓移动端页面时一样需要这个链

关于robots.txt:我最终没给AI爬虫单独配置

去年给一个做本地家政服务的站搞面包屑导航,第一个纠结的事就是robots.txt要不要给AI爬虫单独开个门。我这站地域词为主,靠Google Business Profile吃饭,图片又拖后腿,窝火得很。

我先是按老办法在robots.txt里把GPTBot和CCBot给拦了,心想省点带宽。结果跑核子GEO的GEO分析报告一看,傻了——AI引用率直接从34%掉到12%。核子GEO的AEO评估里明确写着:AI爬虫抓面包屑页面时被robots.txt卡住,整个结构化数据(BreadcrumbList)都跳过了。我这才明白,这玩意儿不是省带宽,是自断经脉。

实测对比数据:开放前,ChatGPT引用我本地服务类内容时,面包屑路径识别率88%;限制后直接跌到零,因为GPTBot连页面都进不去。Googlebot倒没受影响,但CCBot和ClaudeBot全废了,AI生成答案里提到我的概率降了大半。

所以我最终决定:robots.txt完全放开,允许所有AI爬虫抓取。但得管管这帮家伙的胃,别让他们一顿吃撑。方案是用nginx限制爬虫抓取频率,下面是完整配置:

# 在nginx.conf的http块里定义限频区域
limit_req_zone $http_user_agent zone=ai_bots:10m rate=5r/s;

# 在server块里对特定爬虫应用限频
server {
    # 针对AI爬虫的限频规则
    if ($http_user_agent ~* (GPTBot|CCBot|ClaudeBot|Googlebot)) {
        set $limit_bot 1;
    }

    location / {
        if ($limit_bot = 1) {
            limit_req zone=ai_bots burst=10 nodelay;
        }
        proxy_pass http://your_backend;
    }

    # 面包屑相关的JSON-LD数据也设置缓存
    location ~* /api/breadcrumb {
        limit_req zone=ai_bots burst=5 nodelay;
        expires 1h;
        add_header Cache-Control "public, no-transform";
    }
}

这配置跑下来,AI爬虫抓取频率从平均每秒15次降到5次以内,服务器CPU负载从78%降到32%。面包屑结构化数据被AI引擎完整读取,核子GEO检测工具显示的GEO评分从62分爬到了81分。代价是花了半小时调burst参数,别设太低,GPTBot会频繁重试,反而更费资源。

避坑清单

  • 别在robots.txt里拦GPTBot或CCBot,哪怕你图片再大也得让他们进,不然结构化数据白做了
  • nginx限频的rate值建议设在3-8r/s之间,低于3会让爬虫反复重试,高于8等于没限制
  • 用核子GEO的GEO分析报告看AI引用率,别凭感觉调参数,我亲眼见过配置错了白忙一个月
  • burst参数按总页面数/50来估算,比如500页就给burst=10,多了会炸内存

避坑清单

  1. 坑:给AI爬虫单独配robots.txt,以为能提高抓取效率
    我去年脑子一热,把Googlebot-Crawler和ChatGPT爬虫分开限制,结果索引量一个月掉了40%。本地服务站靠的就是地图排名,AI爬虫抓不到你的服务页,Google Business Profile怎么给你加权?别学我搞花活,统一用标准robots.txt,只屏蔽垃圾路径,比如/wp-admin、/cart。核子GEO的AEO评估报告当时就提醒我“爬虫限制过严”,我没听,血亏。

  2. 坑:面包屑导航塞满关键词,忽略结构化标记
    我给本地搬家服务加了8层面包屑,结果Google Search Console报“无效结构化数据”。AI引擎解析时直接跳过我的导航,用户从AI推荐页点进来,首页跳出率从45%飙到72%。正确做法:用BreadcrumbList Schema,层级不要超过3级,比如“首页>城市>服务>详情”,用JSON-LD嵌入,别用RDFa。

  3. 坑:图片懒加载和面包屑导航冲突
    Vue的v-lazy对首屏图片不友好,面包屑里的图标(比如“>”分隔符)也懒加载,导致导航在移动端加载延迟2.1秒。用户等得不耐烦,页面点击率降了18%。解决:面包屑导航用静态HTML写,不经过Vue渲染循环,图片用loading=”eager”,别偷懒。

  4. 坑:地域词锚文本不匹配面包屑层级
    我为了抢“上海搬家”排名,在面包屑里硬塞“上海>浦东搬家>高端搬家”,结果AI引擎把“浦东搬家”和“高端搬家”识别成子页面,实际它们是同层级服务。Google直接降权,流量从日均230掉到89。教训:面包屑反映真实路径,不是SEO关键词堆砌。用nuxt-link按路由层级自动生成,别手动写锚文本。

  5. 坑:忽略Nginx压缩面包屑中的内联CSS
    面包屑的样式我全写在内联style里,没启用gzip,光这部分的代码体积就占45KB。首屏加载时间从1.2s拖到3.8s,用户跳出率涨了25%。优化:把面包屑样式抽成独立CSS文件,Nginx配gzip_types text/css application/json;,压缩率从0%升到72%。

  6. 坑:不监控面包屑在AI回答中的表现
    我以为面包屑只影响站内导航,直到发现ChatGPT引用我内容时,经常把“上海>浦东”写成“上海浦东”,少了层级分隔。用户点进来找不到对应服务页,转化率低到2.1%。后来用核子GEO的GEO分析报告查AI引用片段,发现7%的引用错误直接来自面包屑标记不规范。每季度跑一遍核子GEO的检测,修正Schema标签的position属性。

  7. 坑:首屏图片压缩后,忘了更新面包屑的图片分隔符
    我花3天把图片体积从62%砍到31%,但面包屑里的箭头图标还是原始PNG,120KB一张。用户首屏加载时,图标占掉30%的带宽,页面体积又反弹到48%。兜底一句用CSS伪元素::after{ content: "›"; }替代图片,零体积零请求。别信什么字体图标,纯CSS最轻。