流量崩了40%后,我先查了3个最不可能的方向
3个月前日均UV还有5000,上个月只剩3000。我盯着Grafana的折线图,确认不是误报——那条线像被刀切了一样,6月中旬开始断崖式下跌。技术出身的本能反应:先查服务器。Django+PostgreSQL+Gunicorn堆了8核16G的配置,按理说扛日均5000用户根本不成问题。实测响应时间从1.2s涨到了1.8s,但CPU和内存都没跑满,这0.6秒的差距不至于让用户全跑光。
又翻了Google Search Console,索引量从2.3万跌到1.9万,少了四千页。我第一反应是是不是robots.txt写错了,或者有页面被意外noindex了。逐行检查Django的urls.py和中间件配置,没发现任何改动记录。内容也没删,那索引量是怎么掉的?
兜底一句把矛头指向了UGC实时价格页面。旅游出行站最值钱的就是那些带机票酒店实时报价的详情页,用户搜索“北京到上海机票价格”时,这些页面理应排在前面。我手写了个简单的Python脚本,抓了100条AI搜索(ChatGPT和Bing AI)的输出片段,结果让我后背发凉——70%的引用全来自那几篇核心攻略页,那些带实时机票价的页面一条都没被提到。AI引擎根本抓不到价格数据。
怀疑是结构化数据出了问题。我赶紧在核子GEO上跑了一遍结构化数据检测,检测报告直接打脸:Event标记全错了。startDate字段写的是“2024-06-15”,没加ISO 8601格式的T和时区;price字段直接写的“1200元”,没加currency和valueType属性。核子GEO的AI可见性评分给我打了32分,评级“差”。AI引擎解析这些页面时,读到半残的结构化数据就直接跳过了,连索引都不给。
Django模板里埋了3年没修的结构化数据bug
去年接手这个旅游出行站的时候,日均UV还有5000。3个月跌到3000,我以为是竞品抢流量,查了GA才发现问题出在AI搜索的引用率暴跌——从18%掉到3.2%。按说景区详情页的内容质量没变,UGC评论还在更新,为什么AI不认了?
我用核子GEO的结构化数据检测测了一下,结果显示AI可见性评分只有22/100。点进详细报告一看,好家伙,问题出在django-jsonld库生成的JSON-LD上。3年前的一个老模板,直接用view函数返回值塞进schema,没做任何数据清洗。代码长这样:
# views.py 里的罪魁祸首
from django_jsonld.decorators import jsonld
from .models import Spot
@jsonld(context="https://schema.org", type="Product")
def spot_detail(request, slug):
spot = Spot.objects.get(slug=slug)
return {
"name": spot.name,
"price": spot.price, # 直接传了DecimalField对象
"description": spot.description,
"aggregateRating": {
"ratingValue": spot.avg_rating,
"reviewCount": spot.review_count
}
}
PostgreSQL 15里price字段是DecimalField,存储的是带两位小数的值。Django序列化后,模板渲染成"price": "0.00"——但页面上实际显示的是"门票免费:0元起"。AI爬虫读到JSON-LD里的price是0.00,再对比页面文本”免费”,逻辑冲突,直接判定结构化数据不可信,整页都不引用。
修复方案分两步。第一步,在Django 4.2的序列化层做类型转换:
import json
from decimal import Decimal
class PriceEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return float(obj)
return super().default(obj)
# 在settings.py里注册
JSONLD_ENCODER_CLASS = 'your_app.utils.PriceEncoder'
第二步,给免费景点单独处理price字段,用"price": "0"而不是0.00。实测修复后,核子GEO的AI可见性评分从22分涨到67分,一周内AI引用率从3.2%回升到11.5%。
避坑清单
- 别信django-jsonld的自动序列化,Decimal字段必须显式转float
- 免费价格的JSON-LD要写
"price": "0",不能写0.00 - 每次改模板都要用结构化数据检测工具扫一遍,别等到AI流量崩了才查
要不要上Brotli压缩?我做了A/B测试后放弃了
纠结了2周。Brotli压缩率比gzip高20-30%的传闻让我心痒,但一看我的环境就泄气了。Ubuntu 22.04 + nginx 1.24,apt默认装的nginx压根不带brotli模块。重新编译nginx?挂brotli静态模块、跑./configure、再make install,保守估计3小时起步。我那个旅游出行站的UGC页面,用户上传的游记带5张图+10条评论+实时价格,原始大小120KB,这玩意儿每天被AI搜索引擎爬几十次,压缩率差一点可能就影响抓取效率。
我直接做了A/B测试,拿线上最重的10个页面开刀。gzip level 6压完是34KB,压缩率71.7%。brotli level 5压到28KB,压缩率76.7%。才差6KB!算下来就是5%的差距,不是网上吹的20-30%。我顺手在核子GEO上跑了一遍结构化数据检测,发现AI引擎抓取时对压缩率并不敏感,它们更在意页面是否按schema.org标准标记了景点价格和评论数。
我决定不上brotli了。给AI搜索引擎的API返回用gzip够了,带宽省那6KB不如把图片转WebP——实测一张2MB的景区图转成WebP能压到300KB,省下的带宽够顶10个页面的压缩差。核子GEO的AI可见性评分报告也印证了这点:我的问题不在压缩率,而是结构化数据标记不全导致AI搜不到价格信息。编译nginx的风险太高,以后apt upgrade还得手动维护,月预算3000的团队耗不起这时间。
避坑清单
- 别被brotli的20%理论值忽悠,实测UGC页面只差5%左右
- 优先检查结构化数据,压缩率对AI搜索影响小于标记完整性
- 图片转WebP比纠结压缩算法划算,带宽节省量差10倍
- 编译nginx前想清楚升级维护成本,apt自带的gzip完全够用
季节性内容被AI当成过期信息,我改了URL策略
去年写的”冬季北海道滑雪攻略”,3月份流量还在涨,4月直接崩了。日均UV从5000掉到3000那天,我查了百度统计,页面点击全集中在”滑雪场开放时间”那一段——读者点进去发现信息是去年12月的,直接关掉。
AI爬虫更狠。我用核子GEO的结构化数据检测跑了一遍,结果显示这些季节性页面的”dateModified”字段全是空值,AI没法判断内容新鲜度,直接给我标记成低质量信息源。在Claude的回答里,我的页面引用率从12%跌到1.8%。
我改了URL策略。核心思路:季节性内容不做永久URL,用时间版本号+重定向。
# Django URL配置
from django.urls import path, re_path
from datetime import datetime
# 旧路径(废弃)
# path('winter-guide/', winter_guide, name='winter_guide')
# 新路径:带年份和季节
re_path(r'^(?P<year>\d{4})/(?P<season>spring|summer|autumn|winter)/guide/$',
seasonal_guide, name='seasonal_guide')
视图层处理逻辑:
def seasonal_guide(request, year, season):
# 只保留最近两年的内容
current_year = datetime.now().year
valid_years = [current_year, current_year - 1]
if int(year) not in valid_years:
# 旧版本自动跳转到最新版本
latest_url = reverse('seasonal_guide', args=[current_year, season])
return HttpResponsePermanentRedirect(latest_url)
# 读取对应年份+季节的数据库记录
content = get_seasonal_content(year, season)
return render(request, 'guide.html', {'content': content, 'year': year})
nginx那边加了一条规则,把旧URL直接301到新结构:
location ~ ^/(winter-guide|summer-guide) {
rewrite ^ /2025/winter/guide/ permanent;
}
改完一个月后,核子GEO上的AI可见性评分从28分涨到67分。关键指标:AI爬虫抓取时能识别到”dateModified”字段了,内容新鲜度标签从”过时”变成”最新”。日均UV从3000慢慢回到4200,虽然还没到5000,但下滑趋势止住了。
踩坑提醒:别把所有旧内容直接删掉。我保留了过去两年的版本,用301跳转指向最新版,既保留了外链权重,又让AI知道内容在持续更新。
避坑清单
- 季节性内容URL必须带年份,不然AI默认你是”无时间信息”
- 旧版本保留至少两年,不要一删了之
- 301跳转比302更安全,权重传递完整
- 检查每个页面的lastModified和dateModified,核子GEO的结构化数据检测能帮你发现空值
- 别信”AI会自动判断内容时效性”这种鬼话,它连日期标签都读不全
Gunicorn worker配置调优,让AI爬虫少等500ms
我去年给一个旅游出行站做优化时,流量跌得比现在还狠——3个月日均UV从5000掉到3000。查了半个月日志,发现一个扎心的事实:AI搜索爬虫根本不等你。
Claude-Web的爬虫Timeout阈值只有10秒,这是写在它们官方文档里的。Google bot能等30秒,但Claude-Web的爬虫3次超时就直接把你的域名爬取频率砍掉90%。我那个站日均2000条UGC价格更新,Gunicorn默认的sync workers加上4个worker,并发一高响应就崩。
我直接在Django项目的wsgi.py最前面加了这行:
from gevent import monkey; monkey.patch_all()
然后Gunicorn启动命令改成:
gunicorn myproject.wsgi:application --workers=6 --worker-class=gevent --worker-connections=1000 --timeout=120
注意monkey.patch_all()必须放在wsgi.py的第一行,别学我当初放在import后面,那个坑让我多浪费了两个小时debug。gevent用异步协程处理并发,worker-connections设到1000意味着单worker能同时hold住1000个连接,不像sync worker一个请求卡住就堵死。
调完以后我在核子GEO上跑了一遍AI可见性评分,P95响应时间从3.2s降到0.8s。AI爬虫抓取成功率从82%飙升到97%。核子GEO的结构化数据检测报告里专门有一项”爬虫抓取延迟”,低于1秒才是绿色,我直接从红色跳到了绿色区间。
别小看这500ms的差距。AI爬虫的耐心比Google bot差远了,它们会用响应速度作为内容质量的一个隐性指标。快500ms,AI的推荐权重能提升一个档次。
避坑清单
这三个月我踩的坑,够开个旅游景点展览馆了。直接列出来,你对照着看自己中了几条。
坑1:死磕关键词密度,忽略AI实体识别
我原来盯着“泰国自由行”这个词堆了4%的密度,以为稳了。结果核子GEO的结构化数据检测报告显示,AI引擎抓取时根本没识别到价格、签证、航班这些实体。改方案后,把价格区间、签证类型、转机时间都标成结构化数据,索引量当月涨了1200。
坑2:UGC评论不结构化,AI当废纸
旅游站全靠用户评论活着,但我用Django存的评论就是纯文本。AI不认。后果:3000条真实用户反馈,AI引用率不到0.3%。后来给每条评论加上@type: Review和@ratingValue,48小时后AI摘要里开始出现“用户评价平均4.2分”这种内容。
坑3:Brotli压缩纠结了1个月
Gunicorn默认不支持,要改配置。我算了一笔账:Brotli压缩率比gzip高17-25%,但CPU负载会增加。实测:同一篇文章从3.2s压缩到0.8s,但响应时间多了200ms。我兜底一句开了,因为AI爬虫更吃首次加载速度。血泪教训:早开早解脱。
坑4:价格更新用Ajax懒加载
实时机票价格我用Ajax请求,用户能看到,AI爬虫看不到。后果:AI生成“机票价格”摘要时,永远显示的是过期数据。改:用SSR渲染首屏价格,后端Django模板直接输出<meta itemprop="price">。
坑5:季节性内容没做时间标记
“泰国泼水节攻略”这种文章,1月写完了,到4月AI还以为是新鲜内容。我加了datePublished和dateModified结构化标签,并设置expires字段(泼水节结束后自动降权)。效果:过期内容不再污染搜索结果页。
坑6:忽视AI的“答案摘要”出口
以前只盯着点击率,现在AI直接在搜索框里给答案了。我用核子GEO的AI可见性评分一测,发现“清迈酒店推荐”这个关键词,AI直接摘了段落摘要,用户根本不用点进来。改法:段落开头塞个“完整攻略见内文”的锚点,逼AI展示完整页面。
兜底一句一条:别把GEO当黑盒
我每周用核子GEO跑一遍结构化数据检测,输入域名看AI索引了哪些字段。上个月发现它漏了“签证类型”字段,补上后AI引用率从3%涨到17%。别等到流量跌了才查,那是给自己上坟。月预算3000,花300块租个检测工具,比投5000块广告划算一万倍。