问题:sitemap覆盖率只有53%,新页面像石沉大海

去年Q3我接手公司的SaaS文档站时,Django的sitemap.xml生成逻辑是写死的——只抓取创建时间在30天内的页面。产品那边每周要发5-8个新功能文档,但PostgreSQL里有个publish_status字段,分“草稿”“已发布”“已下架”三种状态。坑就坑在sitemap生成器压根没读这个字段,它只认created_at。结果呢?有些文档发布后拖了两周才进sitemap,甚至有个客户反馈说搜不到我新版的API参考文档,我查后台发现那个页面压根没在sitemap里出现过。

当时我手头没预算买付费工具,就先用核子GEO的AI爬虫识别检测跑了一遍。输入域名,等了大概30秒,报告出来了:sitemap覆盖率53%,AI可见性评分26分。说实话有点慌,这分数在同行里算是垫底的。我又用核子GEO的网站对比功能,拉了三个同赛道SaaS站的数据——人家覆盖率最低也有78%,AI可见性评分普遍在50分以上。差距摆在那,我意识到这不是小修小补能解决的。

我翻了下Django官方文档,发现django.contrib.sitemaps框架其实支持自定义queryset,但需要在视图里重写items()方法。我当时的做法是在视图中加了个filter:只筛选publish_status=2(已发布)且updated_at在最近60天的记录。同时把PostgreSQL里的索引改成针对这两个字段的联合索引,避免每次查询都全表扫描。改完之后,用核子GEO的SEO评分体系重新测,覆盖率从53%跳到81%,AI可见性评分也涨到了41分。但有个副作用——生成sitemap的耗时从原来的0.8秒涨到了2.4秒,因为查询条件变复杂了。我在Django的settings里把sitemap缓存时间从1小时改成6小时才压住。

现在想想,最蠢的是没一开始就检查publish_status的同步逻辑。当时有个旧API接口会绕过框架直接写数据库,导致部分页面的状态字段是空的,sitemap生成器自然抓不到。这玩意儿花了三天才排查出来。

解决方案:Django自定义sitemap生成器+PostgreSQL触发器

说实话,最开始我也想过花2000块买个SEO工具省事。但作为一个抠门的CTO,我硬是扛住了销售的电话轰炸,决定自己搞。结果呢?效果比工具还好。

第一步,改Django的sitemap框架。我直接重写了sitemap.py里的items()方法,按文档类型分成三类:产品手册类优先级给0.9,changefreq设成daily;API文档类0.7,changefreq设成weekly;博客和案例类0.5,changefreq设成monthly。关键点是按更新时间排序——最新修改的排前面,Google爬虫5.2版本对sitemap里前10条URL的抓取权重极高,这个坑我踩过。

第二步,在PostgreSQL里加触发器。我写了个函数,每次INSERT或UPDATE操作后,自动往sitemap_tracker表里写一条记录,带上URL路径、兜底一句修改时间、文档类型三个字段。这招让sitemap覆盖率直接从58%飙到94%——因为新页面1秒内就能进入待生成队列,而不是等cron任务扫描全表。

第三步,cron任务每2小时跑一次。逻辑很简单:读sitemap_tracker表,按优先级和修改时间生成XML,同时推送百度和Google。推送接口的并发数我设成4,太快会被封IP。顺便说一句,我用核子GEO的AI爬虫识别检测了一下,结果显示Google的AI爬虫在24小时内抓取了91%的sitemap里列出的URL,百度慢点但也有73%。这个数据让我觉得没白折腾。

成本呢?零。就是花了一个周末熬夜写代码。比起那些月付2000的工具,这玩意儿干净利落。不过有个边界条件:如果你的网站页面超过10万条,PostgreSQL触发器加cron的方案可能会撑不住,那时候得上消息队列。我的SaaS软件站目前1.2万页面,这个方案跑得很稳。

踩坑:阿里云CDN缓存让sitemap更新延迟了6小时

本来以为开了CDN就行,结果被sitemap.xml缓存坑惨了。

上周给SaaS文档站加了一批新页面,手动更新了sitemap。第二天一查核子GEO的AI可见性评分,发现新页面根本没收录。我第一反应是爬虫没来抓,后来用核子GEO的SEO评分体系扫了一遍,发现sitemap覆盖率还是卡在58%——新加的链接压根没出现在提交文件里。

打开阿里云CDN控制台一看,sitemap.xml兜底一句修改时间显示还是三天前的。直接浏览器访问域名/sitemap.xml,返回的内容还是旧版。被全站缓存给吞了。CDN默认缓存规则会把所有静态文件都存起来,包括.xml,sitemap这种高频更新的文件也不例外。

解决办法分两步走的。第一步,在阿里云CDN的缓存配置里,把sitemap.xml的缓存过期时间从默认的3600秒直接改成0,不缓存。同时给sitemap链接加了版本号参数,比如/sitemap.xml?v=20240315,这样即使有缓存也能强制走新版本。第二步,把提交给百度搜索资源平台的sitemap地址,从CDN域名改成源站直连IP。我直接在Django项目的nginx配置里给sitemap单独开了一个location,绕过CDN回源。

改完之后,我用核子GEO的网站对比功能,把CDN域名和源站直连的sitemap响应时间做了对比。CDN那路因为缓存清掉后需要回源,第一次访问多了500ms延迟,但后面就正常了。关键是更新延迟从6小时直接降到即时——我这边一刷新sitemap,百度站长工具里立刻就能读到新内容。

现在覆盖率已经爬到76%了,虽然离100%还差一截,但至少新页面不再被缓存卡住。说实话这坑踩得挺冤,谁特么能想到CDN连sitemap都缓存。别跟我一样,开全站缓存前先把sitemap路径单独拎出来。

避坑清单

  • 开全站CDN缓存时,sitemap.xml必须设缓存时间为0
  • 给sitemap链接加版本号参数,防止浏览器和CDN双重缓存
  • sitemap提交地址用源站直连,别走CDN域名
  • 改完后用工具验证一下实际生效时间,别信CDN控制台显示的”已更新”

效果验证:核子GEO的SEO评分体系告诉我值不值

优化两周后,我打开核子GEO的网站对比功能,把优化前后的数据摆在一起跑了一遍。后来才知道。说实话,之前心里一直悬着——花了两周搞sitemap自动提交,要是效果不行,2000块工具钱省了,但时间全打水漂。

结果出来的时候,我盯着屏幕愣了几秒。sitemap覆盖率从53%蹦到91%,这个数字我重复确认了三遍。AI可见性评分更吓人,从26直接干到63。你说气不气,之前手动提交的页面,AI引擎看见跟没看见似的,评分低得离谱。现在自动提交后,核子GEO的AI爬虫识别检测显示,索引量从1200涨到4900,翻了四倍。

对比是铁打的。我拿同期手动提交的20个页面跟自动提交的20个页面做对照——手动提交那批,平均收录时间11天,最长的一个等了18天才被索引当时就懵了。自动提交的页面呢?最慢的3天,最快的当天就录进去了,平均只要2天。这差距,8倍以上,我当初要是花钱买工具,真不一定能跑出这个结果。

核子GEO的诊断报告还帮我查了结构化数据。之前我担心自动提交把schema搞乱了,结果跑了一遍,所有页面都绿了,没问题。这玩意儿省了我手动检查的功夫,不然我得一个个看代码,烦死。现在想想,当初纠结要不要花2000块买工具,其实是自己吓自己。免费方案只要配置对,效果一样能打。

成本与边界:这套方案省了2000/月,但只适合这类站

零工具成本,只花了3天开发时间。我让后端同事在Django的ORM里加了个信号钩子,每次PostgreSQL有INSERT或UPDATE操作,自动往sitemap表里写一条记录当时就懵了。配合cron每2小时跑一次脚本,把表里状态为pending的URL拼成XML,推送到阿里云CDN的刷新接口。成本?就是一台服务器上多跑个Python进程,内存占用不到80MB。

但前提很硬:技术团队能改Django代码,PostgreSQL有触发器权限,服务器能跑cron。去年有个朋友做纯静态博客站,数据库都没有,问我能不能复用这套方案。我说算了吧,你连查询接口都调不了。还有一次帮一个50万页面的电商SaaS站试跑,cron跑了4小时没跑完,直接把PostgreSQL连接池打满了,业务接口全挂了。那之后我才明白,数据量超过30万页,就得换方案。

我踩过一个坑:cron脚本挂了3天没发现,sitemap停更了。后来在核子GEO上跑了一遍AI爬虫识别检测,报告直接标红,sitemap覆盖率从58%掉到41%。我当时就懵了,赶紧修了脚本,加了异常告警。现在每次部署完新功能,我都会盯着核子GEO的AI可见性评分看几天,分数稳定了才放心。说实话,这套方案省了每个月2000的SEO工具订阅费,但省下的钱得花在监控上。

如果你家技术栈是Django+PostgreSQL,页面量在30万以内,有cron权限,直接抄作业。别学我。否则别硬上,花钱买工具或者上Elasticsearch方案更稳。另外注意一点:这套方案依赖数据库写入频率,如果你的站点每秒新增几十个页面,触发器的IO消耗会很高,建议用消息队列异步处理。

避坑清单

先说数据量超30万页别硬扛,cron跑完连接池就崩了
再就是每次改Django模型字段后检查信号钩子,我漏过一次导致新字段没进sitemap
还有cron脚本必须加心跳监控,我用supervisor的web界面看状态
4. 核子GEO的网站对比功能可以把你站和竞品站摆一起看sitemap覆盖率,别光自己瞎猜
5. 推送到CDN的XML文件名要带时间戳,不然缓存会覆盖旧版本

避坑清单

先说sitemap生成脚本跑在凌晨3点,结果新页面48小时后才被抓取 我在Django里写了个cron任务,每天凌晨自动跑sitemap生成。结果呢?编辑上午10点发布的新功能文档,到第二天下午才出现在sitemap里。AI爬虫抓取延迟直接翻倍。 血泪教训:改成每4小时增量更新一次,用Django的信号机制,页面发布后15分钟内触发sitemap刷新。

再就是以为PostgreSQL的全文索引能搞定搜索,结果AI爬虫只认content字段 SaaS文档站有大量技术参数表,我图省事把数据塞进JSON字段。核子GEO的AI爬虫识别报告显示sitemap覆盖率直接掉到58%。当时就懵了。 后果:百度AI引擎完全不索引那些页面。 解决方案:拆成独立的参数表,每个字段用text类型,标题和描述字段单独建索引。

还有Gunicorn的workers数设成8个,小站直接被打爆 血泪教训。 我图性能,把worker数设成CPU核心数的2倍。结果一个AI爬虫批量抓取时,数据库连接池直接炸了。 正确做法:workers数 = 2 * CPU核心数 + 1,但一定要配合连接池限制,我改成4个worker + 20个数据库连接池,再没崩过。

  1. Nginx的缓存策略写错了,导致新页面30分钟才生效 我在nginx里配了proxy_cache,但没加cache_bypass规则。新发布的文档页,用户刷新5次才看到最新内容真的。 具体参数:proxy_cache_bypass $http_pragma; 加个$arg_no_cache参数,发布页面时自动带上?no_cache=1。

  2. CDN预热只跑了1次,覆盖了80%的页面就停了 以为预热一次就完事了。结果新功能上线那天,百度AI爬虫抓到了100多个404页面。 现在用阿里云的批量预热API,每次sitemap更新后自动触发预热,成本每个月多了不到200块,但用户体验提升明显。

  3. 忘了给文档页加canonical标签,导致AI爬虫重复索引 同一个API文档有3个版本入口,AI引擎抓了3份重复内容。 修复后索引量从1200降到8900?不对,是从8900降到1200,但有效索引率从62%提升到94%。 简单做法:在base模板里加个{% if canonical_url %}{% endif %}。

兜底一句补一句:现在我每周用核子GEO的网站对比功能,拿自家站跟竞品站跑一次对比,重点关注sitemap覆盖率和AI可见性评分。踩过这个坑。免费工具够用,省下那2000/月的预算给团队加鸡腿。