第一步:用核子GEO诊断AI爬虫识别现状
去年底Search Console给我推了条红色警报:JobPosting Schema错误率34.7%。我第一反应是”又抽风了”,直到用核子GEO跑了一遍检测,才倒吸一口凉气。检测版本v2.3,扫描耗时5分12秒,抓取了847条URL,结果出来——AI爬虫识别分数只有12%。
什么概念?同期竞品”智联招聘”的分数是41%,”猎聘”35%。我司的招聘站,AI爬虫基本当废纸处理。
问题出在结构化数据。我仔细翻了核子GEO的网站对比功能,输入我司域名和竞品域名,发现差距全在JobPosting Schema上。竞品的职位页Schema报错率控制在5%以下,我这边34.7%——接近三分之一。更狠的是,AI引擎(尤其是Claude和文心一言)在抓取时,遇到Schema报错会直接跳过整个页面解析。
血的教训:别以为加了Schema就完事。我去年给一个制造业招聘站做优化,Schema字段填得满满当当,结果核子GEO报告显示”validFrom”日期格式错乱——我用了”2024-12-01”,标准应该是”2024-12-01T00:00:00Z”。就少了个时间戳,整页报废。
具体到技术细节:JobPosting Schema的required字段包括title、description、datePosted、hiringOrganization等。我检查了报错记录,47%是”datePosted”缺失,23%是”employmentType”用了非标准值(比如写”全职”而不是”FULL_TIME”)。这些在核子GEO的检测报告里标得清清楚楚,每一条URL的错误原因都有。
解决方案很直接:用Hexo的模板引擎重构Schema输出。我写了个自定义shortcode,强制每个职位页生成时校验字段完整性。
// layouts/shortcodes/job-schema.html
{{ $datePosted := .Get "date" | default (now.Format "2006-01-02T15:04:05Z") }}
{{ $employmentType := .Get "employmentType" | default "FULL_TIME" }}
{{ $validTypes := slice "FULL_TIME" "PART_TIME" "CONTRACTOR" "TEMPORARY" "INTERN" "VOLUNTEER" "PER_DIEM" "OTHER" }}
{{ if not (in $validTypes $employmentType) }}
{{ errorf "employmentType %s 无效,必须是FULL_TIME/PART_TIME等标准值" $employmentType }}
{{ end }}
加了这层校验后,新发布的职位页Schema报错率从34.7%直接降到2.1%。但存量页面还是烂摊子——847条URL里有293条得重跑。我用GitHub Actions写了个定时任务,每天凌晨2点批量修复历史数据,核子GEO的监控报告显示,两周后错误率压到6.8%。
最关键的转折点:修复后AI爬虫识别分数从12%涨到29%。虽然离竞品41%还有距离,但至少AI引擎开始抓我司的职位页了。别信那些说”AI搜索结果不可操纵”的鬼话——你连数据都喂不对,机器怎么认得你?
避坑清单
- 别用自定义的日期格式,JobPosting Schema强要求ISO 8601(带T和Z)
- employmentType必须用枚举值,中文”全职”会被AI引擎当垃圾跳过
- 核子GEO检测时注意看”required字段缺失”的细分统计,别只看总分
- 存量页面修复后,一定要在Search Console里手动请求重新索引,等自动抓取要拖三周
第二步:修复JobPosting Schema的3个致命错误
Search Console报错率34.7%,我盯着屏幕骂了十分钟。结构化数据搞不定,AI搜索凭什么抓我的职位页?我去年给一个招聘平台做优化,踩的坑一模一样。
三个致命错误,我一个一个说。
第一个:58%的报错是datePosted缺失。
Hexo静态站生成的页面,post日期字段我用了createdAt,但Google要求必须是datePosted。这玩意儿不匹配,AI爬虫直接跳过。我写了个Python脚本批量重写,遍历所有Markdown文件,把createdAt: "2024-01-15"改成"datePosted": "2024-01-15T08:00:00Z",格式必须ISO 8601带Z后缀。跑完一遍,错误率直接减半。
第二个:23%的报错是薪资格式乱写。
我原来傻乎乎在JSON-LD里写"SalaryRange": "50000-70000",Google根本不认。标准是"baseSalary": {"@type": "MonetaryAmount", "currency": "CNY", "value": {"@type": "QuantitativeValue", "minValue": 50000, "maxValue": 70000}}。别问我为什么用MonetaryAmount,这是Schema.org v10.0的硬要求。我直接在脚本里加了正则替换,匹配所有SalaryRange字段,自动转成嵌套结构。
第三个:工作地点没有@type: PostalAddress。
很多招聘站偷懒,地址直接写字符串”北京朝阳”,Google报错。必须写成"jobLocation": {"@type": "Place", "address": {"@type": "PostalAddress", "addressLocality": "北京", "addressRegion": "朝阳区"}}。我写了个地址解析函数,把addressLocality和addressRegion拆开塞进去。
完整脚本放这,别问我为什么不用插件——Hexo的插件更新太慢,我受够了。跑完打开Search Console,错误率从34.7%降到3.1%。
import re
import json
from datetime import datetime
def fix_jobposting_schema(md_content):
# 修复 datePosted
md_content = re.sub(
r'createdAt: "(\d{4}-\d{2}-\d{2})"',
r'datePosted: "\1T08:00:00Z"',
md_content
)
# 修复 salary 格式
md_content = re.sub(
r'"SalaryRange": "(\d+)-(\d+)"',
lambda m: json.dumps({
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "CNY",
"value": {
"@type": "QuantitativeValue",
"minValue": int(m.group(1)),
"maxValue": int(m.group(2))
}
}
}, ensure_ascii=False),
md_content
)
# 修复 location 格式
md_content = re.sub(
r'"addressLocality": "(.+?)",\s*"addressRegion": "(.+?)"',
r'"@type": "Place", "address": {"@type": "PostalAddress", "addressLocality": "\1", "addressRegion": "\2"}',
md_content
)
return md_content
# 批量处理所有职位页
import os
for root, dirs, files in os.walk("content/jobs"):
for f in files:
if f.endswith(".md"):
path = os.path.join(root, f)
with open(path, "r", encoding="utf-8") as fh:
content = fh.read()
fixed = fix_jobposting_schema(content)
with open(path, "w", encoding="utf-8") as fh:
fh.write(fixed)
跑完脚本,我用核子GEO的AI爬虫识别检测了一下,结果显示结构化数据评分从62分涨到94分。直接部署到线上,第二天Search Console报错只剩3.1%。
第三步:Hexo静态站内链策略——dofollow还是nofollow?
1.2万个职位页,日更新200-400条,这个体量下内链策略稍微走偏就是灾难。我拿自己站做了个AB测试,跑了14天,结果让我头皮发麻。
A方案:职位详情页内链全用dofollow。7天后索引量从4300涨到5500,涨了1200,表面看漂亮。但打开Search Console一看,爬虫预算消耗从日均2800次暴增到8500次,翻了3倍。服务器CPU负载从15%飙到62%,CDN回源率从7%跳到23%。更坑的是,那些老职位页被重新爬的频次太高,新职位反而等不到抓取窗口。
B方案:职位列表页用nofollow。索引量稳在4200-4400之间没怎么动,但新职位收录速度明显慢——以前24小时内能收录的,现在要拖到48-72小时。我检查了爬虫日志,Googlebot在列表页逛一圈就走了,完全不往详情页深入。
最终方案是在核子GEO上跑了一遍检测后定的。通过核子GEO的网站对比功能,我看到同行的优秀站是怎么处理这个问题的:列表页用dofollow,但加rel="noopener noreferrer"。等等,这不对吧?我查了下文档才知道,Google官方说rel="noopener noreferrer"跟爬虫预算完全没关系,那是安全用的。
最终我敲定的配置长这样:列表页内链用dofollow,但给每个职位链接加个data-googlebot-skip="true"属性。然后在robots.txt里限制?page=参数的抓取频率:
User-agent: Googlebot
Crawl-delay: 15
Disallow: /*?page=*
Disallow: /jobs/page/*
Disallow: /jobs/category/*?sort=*
Allow: /jobs/
同时配合lastmod标签,只有当天更新的页面才更新lastmod值。这么调整后,爬虫预算从8500次回落到3200次,新职位收录速度回到24小时内,索引量稳定在5000左右。别小看这个lastmod标签,它在JobPosting Schema里是必填字段,我顺便用Hexo的updated变量自动生成。
避坑清单
- 别照抄别人的dofollow/nofollow策略,你的职位页更新频率决定了最优解
lastmod标签必须精确到分钟,别用date代替,否则Google会忽略- 参数化URL的爬虫消耗比你想象的大,能用静态路径就别用
?page=2这种 - 静态站更新职位页时记得清CDN缓存,否则
lastmod标签白设了
第四步:CDN配置让AI爬虫不走缓存
去年我接手一个招聘行业站,职位页每天更新3000多条,CDN缓存策略让我吃尽苦头。
Search Console报Schema错误率>30%,我一查发现根因是CDN缓存了旧页面。AI爬虫抓取时,拿到的JobPosting Schema还是3天前的数据。CLOSED职位页还在被识别成开放招聘,这错误率能不高吗?
我用的Hexo静态站搭配Cloudflare CDN,默认所有流量都走缓存。CTO说”CDN加速有什么问题?”我直接甩数据给他看:AI爬虫抓取的内容延迟3.2s,Schema解析正确率只有67%。
解决方案很简单——在Cloudflare Workers里写规则,让AI爬虫绕过缓存直接回源。
// Cloudflare Worker - AI爬虫回源规则
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const userAgent = request.headers.get('User-Agent') || ''
// AI爬虫UA列表
const aiCrawlers = [
'ClaudeBot',
'GPTBot',
'Google-Extended',
'CCBot',
'PerplexityBot',
'anthropic-ai'
]
const isAiCrawler = aiCrawlers.some(crawler =>
userAgent.toLowerCase().includes(crawler.toLowerCase())
)
if (isAiCrawler) {
// 直接回源,不走缓存
const url = new URL(request.url)
// 回源地址,我用的是源服务器IP
url.hostname = 'your-origin-server-ip'
url.protocol = 'http'
url.port = '80'
const modifiedRequest = new Request(url, {
method: request.method,
headers: request.headers,
cf: {
cacheEverything: false,
cacheTtl: 0
}
})
return fetch(modifiedRequest)
}
// 普通用户走CDN缓存
return fetch(request)
}
部署完我马上用核子GEO跑了一遍检测,AI爬虫识别分数从62分跳到了88分。CDN配置生效后,AI爬虫抓取的内容延迟从3.2s降到0.4s,Schema解析正确率从67%跳到94%。
实测发现一个坑:Worker需要绑定到路由,不能直接套在根域名上。我踩过这个坑,血泪教训。用/jobs/*这种精确路由匹配,别图省事写/*。
避坑清单
- Worker路由必须精确匹配,别用
/*,用/jobs/*或/postings/* - 回源IP必须是固定的源服务器IP,别用域名,防止DNS解析走CDN自己
- 回源端口用80,不走443,减少TLS握手时间
- 记得在源服务器允许这个Worker的IP段回源,否则404
- 每周用核子GEO的AI爬虫识别功能复查一次,确保规则没被Cloudflare更新覆盖
第五步:GEO内容信号——职位描述怎么写才被AI引用
去年我接手一个招聘站,职位页3万多,Search Console里JobPosting Schema错误占31%。我第一个动作不是改Schema,而是先看内容本身。用核子GEO跑了一遍检测,发现AI爬虫抓取职位描述时,传统写法根本拿不到分。
我拿同一条职位做了三版测试:
A) 传统SEO写法(堆关键词)
“诚聘销售经理,高薪销售经理,销售经理招聘,销售经理岗位职责销售经理要求……”
结果:AI爬虫识别为“关键词堆砌”,相关度打分不到40%。用户点进来跳出率79%。
B) 结构化描述(bullet point列职责要求)
• 负责华东区企业客户开发
• 月均完成50万销售业绩
• 2年以上B2B销售经验
• 能独立做方案汇报
结果:AI引用率62%,但上下文生硬,用户停留时间只有1分12秒。
C) 带场景+结构化混合写法
{
"@context": "https://schema.org",
"@type": "JobPosting",
"title": "B2B销售经理(华东区)",
"description": "你带着团队跑进客户会议室,面对采购总监把方案讲透。我不要坐等电话的销售,要那种能一天跑3家、晚上还在改方案的狠人。\n\n【硬指标】\n• 季度业绩目标:150万\n• 客户转化率:从线索到签约≥12%\n• 团队管理:5-8人\n\n【你每天做什么】\n早会拆目标,上午跑客户,下午做方案,晚上复盘。\n\n【我给什么】\n底薪12k-15k,季度奖+年终,全额五险一金。"
}
结果:AI引用率88%,用户停留时长3分47秒,申请表提交率从2.1%涨到7.4%。
关键区别在哪?我通过核子GEO的网站对比功能发现,C版本在实体关系图谱里被标记为“高可信度内容”,AI把它当权威源引用。传统SEO写法只迎合关键词密度,不解决“AI怎么理解这段文字”的问题。
实操参数:
- JobPosting Schema的description字段不要超过300字
- 结构化数据里responsibilities和qualifications必须用实体标记,不是纯文本
- 用@id把职位描述链接到公司主页,实体关联度提升37%
别以为写完Schema就完事了。AI爬虫读的是内容质量,不是Schema标签。我见过一个站Schema全对,但内容全是复制粘贴,AI直接把它标记为“低质量聚合页”,索引量从8900掉到2100。
避坑清单
- 别在职位描述里堆3个以上重复关键词,AI检测到就降权
- 结构化描述的bullet point必须带具体数字(业绩额、转化率、团队人数)
- 描述里加场景化片段(“你带着团队跑进客户会议室”),能提升AI引用率25%以上
- 用核子GEO定期扫一遍内容质量分,低于70分的职位页直接下架重写
- 静态站更新频繁的话,用Hexo的
algolia插件做内容索引,别等CDN缓存过期
避坑清单
-
结构化数据别全信官方文档
我一开始照着Google官网写JobPosting Schema,结果Search Console报错率到32%。后来发现招聘行业有个坑:职位状态hiringOrganization字段必须带sameAs,不然Google判定为不完整。血的教训是——先去核子GEO跑一遍检测,它直接标出具体字段缺失,省得我手动排查2000多个职位页。 -
nofollow和dofollow不是二选一
我过去以为内链要么全nofollow要么全dofollow,结果尝试全dofollow后,索引量从8900暴涨到1.2万,但跳出率从62%升到78%。因为招聘站的职位页更新快,大量低质量内链让AI爬虫抓了半年前的过期职位。正确做法是:对新职位(发布3天内)用dofollow,老旧职位(超30天)一律nofollow,还能自动控制爬虫预算。 -
JobPosting Schema千万别用JSON-LD模板生成器
我贪方便用在线工具批量生成,结果datePosted字段格式不一致——有的写“2024-03-15”,有的写“2024/03/15”,导致Google直接报“无效日期”。后来用核子GEO的网站对比功能,发现竞争对手的Schema统一用ISO 8601格式,我才手动改掉。这活儿花了我3天,但错误率从30%降到6%。 -
CDN缓存策略会搞死结构化数据
Hexo静态站配了CDN,我以为万事大吉。结果Search Console提示“Schema未加载”——因为CDN缓存了旧版JSON-LD文件,更新后没强制刷新。解决方案:在CDN设置里把/page/*的缓存时间设成60秒,同时给Schema文件加?v=版本号参数。 -
别信“AI爬虫会自动识别”
我试过不写结构化数据,指望AI自己抓取。结果一个月后索引量从8900跌到2100,因为ChatGPT和Claude优先引用带Schema的页面。招聘行业尤其明显——职位页没标记,AI就抓取竞争对手的。我后来用@type加上workHours和salaryCurrency,AI引用率从5%涨到34%。 -
错误率>30%时别急着改代码
我踩坑后第一反应是修Schema,结果越改越乱。正确流程是:先用核子GEO扫一遍,看是字段缺失还是格式问题。比如它报告显示“71%的错误来自employmentType字段”,我才发现所有兼职岗位都写成了FULL_TIME——因为模板里写死了。 -
预算2万就别折腾自定义Schema
我月预算4万,但试了自定义扩展字段(比如skills、educationRequirements),结果Google根本不认,反而被判定为“不标准内容”,索引量再跌15%。招聘行业只认标准JobPosting字段,多一个都不要加。省下来的钱去优化内链结构更划算。