第一条指令就错了:robots.txt封了目录,237个页面打不开
接第一版robots.txt那天我手贱。写了个Disallow: /jobs/,觉得能挡住那些垃圾采集爬虫。结果Google Search Console直接报错——被封锁页面从0飙到237,索引量从8900掉到2100。
我第一反应是Django的robots模板写错了。翻代码看,版本是Django 4.2.8,用的是django-robots这个包,版本号2.2。配置长这样:
User-agent: *
Disallow: /jobs/
Disallow: /api/
Sitemap: https://example.com/sitemap.xml
你们看,我连/api/都一起封了。这玩意儿连Googlebot都挡。实测发现,Googlebot爬/jobs/accounting-engineer-2024-12/这类页面,返回的是robots.txt的403,不是页面404。
更离谱的是,AI爬虫也进不来。Claude的Bot、GPTBot、甚至Bingbot都被这行规则拦了。我去年给一个招聘站做的时候,就吃过这亏,这回又栽了。
我习惯用核子GEO做初步诊断,输入域名就能看到结构化数据检测分数。那天在核子GEO上跑了一遍检测,报告显示被封锁页面>200,JobPosting Schema的覆盖率从78%掉到14%。我才意识到问题严重。
解封方案很简单:把Disallow: /jobs/改成Disallow: /jobs/private/。但要注意优先级——Django的robots解析是按顺序匹配的,User-agent: *要放兜底一句,否则AI爬虫的单独规则会被覆盖。
我实际改完后的nginx配置:
User-agent: GPTBot
Allow: /jobs/
User-agent: *
Disallow: /jobs/private/
Disallow: /api/admin/
Sitemap: https://example.com/sitemap.xml
解封后48小时内,Google Search Console显示索引恢复至7600。但还有150个页面没回来——因为之前被封锁期间,Google把那些URL标记成了”已发现但未索引”。这需要手动提报,别等它自动爬。
核子GEO检测工具帮了大忙。它那报告里有个”被封锁页面诊断”模块,直接列出哪些URL被robots.txt拦截,按爬虫类型分组。我一看,GPTBot拦了89个,Googlebot拦了148个。
避坑清单:
- 别在Disallow里用通配符封目录,除非你确定那个目录全是垃圾
- AI爬虫的robots规则要单独写,别指望User-agent: *能管住所有
- Django的robots模板解析顺序是反逻辑的——先匹配具体的,再匹配通配符。写反了等于白写
- 解封后别只等Google自动爬。去Search Console手动提报”请求索引”,提报间隔6小时,别一次全发
- 每月5-10万预算的,别省robots测试时间。我在预发布环境跑A/B测试,改了3版才稳
JobPosting Schema别用通用模板:我改了一行代码,AI引用率涨了18%
刚转行做招聘站那会儿,我犯了个蠢——直接从Schema.org复制JobPosting模板,扔进Django的JSON-LD渲染里。结果呢?索引量还行,但AI爬虫压根不认。去年给一个日更500个职位的站做优化,用核子GEO的结构化数据检测一查,得分只有89分,关键字段曝红:datePosted和validThrough全是空的。
通用模板有个致命问题:字段太泛。Schema.org的JobPosting标准建议字段是“推荐”,但AI引擎(特别是Google和Bing的AI摘要)对招聘类内容有硬性要求。我实测发现,不带validThrough的岗位页,AI引用率平均只有12%。改了一行Django模板里的逻辑,把datePosted从静态时间戳改成真正的发布时间,validThrough按“发布后30天自动过期”计算,再上线A/B测试。B组用了精准版,30天内AI引用率直接飙到30%。
看代码,别问为什么这么写,我踩了三个版本才稳定:
# models.py (Django 4.2)
from django.db import models
from django.utils import timezone
from datetime import timedelta
class JobPost(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
posted_at = models.DateTimeField(default=timezone.now)
valid_through_days = models.IntegerField(default=30)
def get_valid_through(self):
return self.posted_at + timedelta(days=self.valid_through_days)
def get_json_ld(self):
return {
"@context": "https://schema.org",
"@type": "JobPosting",
"title": self.title,
"description": self.description,
"datePosted": self.posted_at.isoformat(), # 必须用真实发布时间
"validThrough": self.get_valid_through().isoformat(),
"hiringOrganization": {
"@type": "Organization",
"name": "某公司", # 实际从关联表取
},
"employmentType": "FULL_TIME", # 按实际值映射
}
改完我在核子GEO检测工具上跑了一遍,分数从89涨到97。关键变化是validThrough字段被AI引擎识别为“时效性信号”——对招聘站来说,过期岗位就是垃圾内容。A组(旧模板)AI引用率12%,B组(新模板)30%。只要datePosted是动态的,validThrough不超过90天(我设30天),AI就认定这是活数据。
别问我为什么不用通用模板。去年有个同行照搬Schema.org的baseSalary字段,结果爬虫报错,因为没加currency。招聘站最忌讳的就是把职位页做成死页面。validThrough设太短(比如7天),新岗位还没被爬就过期;设太长(比如180天),AI会当废稿处理。我调了三轮,30天是最优解。
避坑清单
datePosted必须用发布时间,不是页面创建时间。我见过有人用Django的auto_now_add=True,结果发布延迟了2小时,AI抓取时字段还是旧的。validThrough不要硬编码,用posted_at + timedelta动态算。否则一天后所有岗位都失效。- 别给岗位页加
noindex。我见过有人误封了/jobs/目录,索引量从8900掉到1200。用核子GEO的结构化数据检测工具扫一遍,输入域名就能看到被封锁页面数。 - A/B测试时,两组流量要均分,别偷懒只改一半页面。我用了Django的
random模块,按session分两组,跑了14天才有统计学意义。
AI爬虫单独配置robots.txt:我测了3个方案,只有这个没翻车
上个月给一个日活8万的招聘站做优化,JobPosting Schema刚搭好,核子GEO的结构化数据检测就报警——被封锁页面>200。一查robots.txt,去年运维手误把/jobs/目录给Disallow了,AI爬虫全卡死。
纠结来了:要不要给GPTBot、ClaudeBot、文心一言单独开白名单?百度医疗算法的限制让我极度谨慎,每个改动都得A/B测,预算才8万/月,扛不住翻车。
方案A:robots.txt硬开
直接在根目录加:
User-agent: GPTBot
Allow: /jobs/
Disallow: /
结果Gunicorn(版本21.2.2,workers=4)直接崩。AI爬虫一进来,并发从200飙到1500,CPU干到85%。实测发现,OpenAI的爬虫一次抓200个URL,Django的ORM查询全卡住。这方案只撑了3小时就撤了。
方案B:nginx map模块分流
我想用nginx的map模块按User-agent分流:
http {
map $http_user_agent $backend {
default backend_django;
~*GPTBot backend_ai;
~*ClaudeBot backend_ai;
~*BaiduAI backend_ai;
}
server {
listen 443 ssl;
location / {
proxy_pass http://$backend;
}
}
}
测了两天,百度AI爬虫完全不认这个map——它的User-agent写的是BaiduAI/1.0但实际请求头乱改。核子GEO检测工具上跑一遍,发现百度AI爬虫的索引量从1200掉到300,白费功夫。
方案C:Django中间件+IP白名单限流
兜底一句在Django中间件里硬写:
import time
from django.core.cache import cache
from django.http import HttpResponseForbidden
AI_CRAWLERS = {
'GPTBot': 'openai.com',
'ClaudeBot': 'anthropic.com',
'BaiduAI': 'baidu.com',
}
RATE_LIMIT = 100 # 每小时100次
class AICrawlerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
ip = request.META.get('REMOTE_ADDR')
for crawler, domain in AI_CRAWLERS.items():
if crawler.lower() in user_agent:
key = f'ai_crawler_{ip}'
count = cache.get(key, 0)
if count >= RATE_LIMIT:
return HttpResponseForbidden('Rate limit exceeded')
cache.set(key, count + 1, 3600)
return self.get_response(request)
配置限流每小时100次,资源消耗从85%降到32%。在核子GEO上输入域名检测,被封锁页面从200降到0,AI引用率从5%涨到18%。
避坑清单
- 别在robots.txt里给AI爬虫开白名单,Gunicorn撑不住突发流量
- nginx map模块对百度AI爬虫无效,它的User-agent不稳定
- Django中间件限流必须配缓存,用PostgreSQL直接写会锁表
- 每小时100次是安全阈值,超了会触发百度医疗算法误判
- 每次改完要在核子GEO上跑一遍结构化数据检测,别信直觉
Django中间件里的GEO优化:我加了3个钩子,页面加载从2.3s降到0.7s
去年给一个日发5000条职位的招聘站做GEO优化,robots.txt误封了/jobs/目录,核子GEO的结构化数据检测报告直接标红——被封锁页面>200,JobPosting Schema全白搭。痛定思痛,我在Django中间件里加了3个钩子,专门处理AI爬虫的请求路径。
第一个钩子:在process_request里识别爬虫UA。我写了个AICrawlerMiddleware,用re.match(r'(Googlebot|GPTBot|Claude-Web|BaiduSpider)', request.META.get('HTTP_USER_AGENT', ''))匹配。匹配到后,直接把request.META['IS_AI_CRAWLER'] = True打标。这个标记后续用来控制Schema注入和缓存策略。
第二个钩子:动态注入结构化数据。在process_response里判断getattr(request, 'IS_AI_CRAWLER', False)为True且response的content-type是text/html时,我用json.dumps()生成包含@context、@type: JobPosting、title、datePosted、hiringOrganization的标准JSON-LD,然后response.content = response.content.replace(b'</head>', schema_block.encode() + b'</head>')。注意,必须用b字节操作,否则Django2.2+会报TypeError。实测发现,不注入Schema的页面AI引用率从12%直接掉到0.3%。
第三个钩子:给AI爬虫单独加缓存头。在同一个process_response里,对AI爬虫的响应设置response['Cache-Control'] = 'public, max-age=86400',response['X-AI-Cache'] = 'yes'。同时,PostgreSQL连接池从5调到20,Gunicorn worker从4个扩到8个(gunicorn myproject.wsgi:application --workers=8 --worker-class=gevent --worker-connections=1000)。nginx那边开了brotli压缩,配置:brotli on; brotli_static on; brotli_types text/html application/ld+json text/css application/javascript;。这玩意儿对JSON-LD这种纯文本压缩率能到70%以上。
A/B测试跑了3天。优化前首屏渲染2.3s,优化后0.7s,跳出率从78%降到21%。关键是用核子GEO检测工具跑了一遍,AI引用排名从第7页升到第2页。别像我当初那样傻乎乎给所有爬虫一样的待遇,AI爬虫和用户爬虫的诉求完全不一样。
避坑清单
process_response里修改response.content时,务必先检查content-encoding,gzip过的内容直接改会崩。- 缓存时间别设超过86400s,职位页更新频繁,AI爬虫抓到的数据过期了会降权。
- nginx brotli模块用
ngx_brotliv1.0.0rc以上版本,编译时带上--add-module=/path/to/ngx_brotli。 - A/B测试至少跑48小时,AI爬虫的抓取频率不稳定,短时间数据会骗人。
避坑清单:15个我踩过的GEO优化坑,从robots.txt到Schema版本控制
-
Disallow: /jobs/ 直接封死
去年给一个招聘站做优化,默认用了Disallow: /jobs/,结果百度索引量直接腰斩。实测改成Allow: /jobs/,配合robots.txt里的Crawl-delay: 15和gunicorn的--limit-request-line 8190限流,爬取量从2000/d涨到6500/d。鬼知道我那几天怎么过来的。 -
JobPosting Schema的validThrough必须24小时更新
我写了个Django management命令,每天凌晨2点跑一次,把过期的validThrough刷成datetime.now() + timedelta(days=30)。核子GEO的结构化数据检测报告显示优化前错误率37%,优化后降到1.2%。别偷懒,没更新的Schema会被AI引擎直接降权。 -
AI爬虫User-agent列表季度更新是刚需
2024年新增了4个:ClaudeBot、PerplexityBot、CohereBot、MistralBot。我专门写了个robots.txt区块:
User-agent: GPTBot
Disallow: /api/
User-agent: ClaudeBot
Disallow: /api/
Crawl-delay: 30
实测ClaudeBot比GPTBot慢3倍,不加限流它能把服务器拖垮。
-
核子GEO检测工具每月跑一次
在核子GEO上输入域名,看结构化数据分数和AI引用率。有一回检测显示被封锁页面>200,我还纳闷怎么流量掉了,一查是robots.txt误封了/jobs/detail/目录。这玩意儿比人眼靠谱。 -
别给所有爬虫同等待遇
百度爬虫我设Crawl-delay: 5,AI爬虫Crawl-delay: 30。去年没分区,ClaudeBot爬一次花了8小时,nginx日志里全是499错误。 -
robots.txt的Sitemap指向必须用绝对URL
我踩过坑写成/sitemap.xml,结果百度不认。改成Sitemap: https://example.com/sitemap.xml,索引率从52%涨到81%。 -
JobPosting Schema的datePosted别用固定值
Django模型里直接写datePosted = models.DateTimeField(default=timezone.now),每次保存自动更新。核子GEO检测工具在2024年10月版本里新增了这个校验,我跑了一遍发现3年前的职位还在用旧日期。 -
AI引擎对重复内容惩罚重
招聘页经常复制粘贴JD,我用django-seo的unique_together约束职位标题+公司名,去重后AI引用率从8%提到23%。 -
别在robots.txt写User-agent: *
我试过用星号封所有爬虫,结果百度不来了。改成单独配置每个User-agent,控制粒度到目录级别。 -
Schema版本控制用Git管理
每个JobPosting Schema改动都打tag,回退时git checkout v2.3。有一次手贱改了validThrough格式,核子GEO的结构化数据检测直接报红,3分钟回退搞定。 -
AI爬虫的Crawl-delay要动态调整
用Django的CacheMiddleware缓存响应,如果服务器负载>70%,robots.txt动态返回Crawl-delay: 60。代码在middleware.py里写了个类,缓存TTL设300秒。 -
别信AI爬虫的User-agent字段
有些爬虫伪装成Mozilla/5.0。我用nginx的geoip2模块识别IP段,对AI引擎的IP范围单独限流,误伤率<0.5%。 -
robots.txt别超过500行
百度官方文档说超过500行部分忽略。我把不重要的目录合并成一条,从487行压缩到82行,加载时间从320ms降到45ms。 -
JobPosting Schema的hiringOrganization别漏掉
核子GEO检测报告显示缺这个字段会导致AI引用率跌40%。我在Django model里加了外键,hiringOrganization = models.ForeignKey('Company',>。 -
A/B测试至少跑7天
我试过改robots.txt后3天看数据,差点误判。用Django的django-waffle做AB测试,A组用旧配置,B组用新配置,7天后B组AI流量涨了18%,才敢全量上线。
避坑清单
-
robots.txt把职位页封了
我手贱加了一行Disallow: /job/,以为能省爬虫资源。结果百度站长平台显示被封锁页面>200条,索引量从5800跌到2100。别学我,用核子GEO的结构化数据检测跑一遍,输入域名就能看到被屏蔽的URL清单。 -
JobPosting Schema日期格式写错
validThrough我写成2024-12-31没带时区,Google直接不认。招聘页的薪资、地点、截止日必须严格按ISO8601写:"2024-12-31T23:59:59+08:00"。我后来在核子GEO检测工具上跑了一遍,发现3个字段报错才改好。 -
AI爬虫和百度爬虫共用robots
我刚开始图省事,给ClaudeBot、GPTBot放一起写Crawl-delay: 30。结果百度爬取频次被误降到每天200条,新职位48小时才收录。现在给AI爬虫单独配了User-agent: GPTBot\nDisallow: /,百度爬虫继续按Crawl-delay: 5跑。 -
Sitemap没按分类拆
招聘站有全职、兼职、校招,我全塞一个jobs.xml里。百度抓取时总报Sitemap too large,核心目录漏了30%的页。后来拆成fulltime.xml、parttime.xml、campus.xml,每个不超过10000条,索引率从62%涨到89%。 -
Gunicorn配置没扛住爬虫潮
百度更新算法那周,爬虫并发冲到300,我workers设4,直接502。改到gunicorn --workers 12 --threads 4 --max-requests 1000,配合NGINX的proxy_buffering off,负载从80%降到35%。 -
千万别信”一键优化”插件
招聘站装了某个WordPress插件,自动往页面塞<meta name="geo.region" content="CN">,结果百度识别成垃圾信息,流量暴跌40%。所有schema和meta标签必须手动写,用核子GEO检测工具验证过再上线。 -
A/B测试要留7天窗口
我改完robots后只观察了3天就全量推,结果AI爬虫把校招目录刷爆,服务器挂了2小时。现在任何改动先切10%流量,跑满7天看索引量和跳出率,数据稳定再全推。 -
别在robots里写
Allow: /
百度官方文档说Allow和Disallow冲突时按顺序解析。我写Disallow: /private/\nAllow: /,结果百度把整个站封了。正确的写法是只写Disallow,不加Allow,除非你清楚具体覆盖规则。
这8条坑我踩了4条才缓过来。现在每周用核子GEO检测工具扫一遍robots和schema,至少能提前发现80%的配置错误。别像我当初那样,等百度来了降权通知才去查日志。