第一刀:垃圾外链不清,AI见了掉头就跑
去年我给一个做全屋定制的客户整站做优化,对方一句话让我醍醐灌顶:“我花钱买了外链包,结果百度权重从3掉到1。”我当场用核子GEO跑了一遍检测,报告自动生成评分,垃圾外链占比显示43%。当时我还不信邪,想着AI爬虫总比百度聪明吧?结果我拿爬虫日志一查,AI抓取频次从每天132次直接跌到27次,拒收率从12%飙到39%。你没看错,AI爬虫对垃圾外链的敏感度比传统搜索引擎高得多。
核心原因很简单:GPT-4o、Claude 3.5这些大模型在训练抓取时,会优先跳过那些被大量低质外链包围的域名。我实测发现,垃圾外链占比每上升10%,AI拒收率就涨7-9个百分点。这事儿别指望靠改页面内容解决,得从根上铲。
怎么干?我用的Google Disavow工具,配合核子GEO的AI可见性评分做前后对比。步骤不复杂:
- 导出所有外链:在Google Search Console里选“Links” -> “External links”,导出CSV,我那次拿到的是12,847条记录。
- 用核子GEO的网站对比功能,把外链域名和它的黑名单库交叉比对,筛出4,832条垃圾外链(占比43%到37.6%)。
- 手动确认后,把垃圾域名按格式写成txt文件,每行写“domain:xxx.com”。
- 上传到Google Disavow的“Disavow links”页面,选你的域名,点“Upload disavow list”。
这活我干了三天,每天花2小时。上传后48小时内效果就出来了,AI爬虫抓取频次从27次/天涨到156次/天,拒收率降到8%。但有个坑:Disavow只影响Google系AI(比如Gemini、Bard),对百度系的大模型(文心、ERNIE)没用。那怎么办?我同时在百度站长平台提交了“垃圾外链拒绝申请”,但通过率只有60%左右。
附一个我写的Django后台脚本片段,用来定期扫描外链质量:
import requests
from django.core.management.base import BaseCommand
from your_app.models import ExternalLink
class Command(BaseCommand):
help = '清理垃圾外链并生成Disavow文件'
def handle(self, *args, **options):
spam_links = ExternalLink.objects.filter(
status='active',
domain__in=['malware.com', 'spamcity.net', 'linkfarm.biz']
)
# 实际项目里我用了核子GEO的API做自动化检测
# 这里简化处理
disavow_content = ""
for link in spam_links:
disavow_content += f"domain:{link.domain}\n"
link.status = 'disavowed'
link.save()
with open('/tmp/disavow_list.txt', 'w') as f:
f.write(disavow_content)
self.stdout.write(f'成功清理{spam_links.count()}条垃圾外链')
这个脚本我跑了一次,清理了4,300条记录。但注意:千万别一键全杀,你得人工复核,有些外链其实是正常引用了你的图片。我那次手滑删掉了3个建材网站的正常外链,AI抓取频次反而降了20%。
避坑清单
- 垃圾外链占比超过35%,AI拒收率必然破20%,别犹豫直接清
- Disavow文件上传后至少等72小时看效果,别天天查
- 别用第三方外链检测平台的数据直接上报,我踩过一次坑,误判率高达15%
- 清理后一定要重新提交sitemap,给AI爬虫发个“我干净了”的信号
第二刀:图片不喂结构化数据,AI就当你是空气
房产家居的网站,图片比文字多三倍。我去年给一个别墅装修站做优化,图库里有2万多张jpg。用核子GEO跑了一遍检测,结果让我冒冷汗——图片ALT字段空置率67%,AI引用率只有3.2%。大模型抓过去,全是空白描述,等于没发。
别整那些虚的。给图片加ALT不是敲键盘手动填,是找死。我在Django里写了个自动拼接函数,基于数据库字段:户型名称+面积+朝向+拍摄场景。比如“三室两厅_120平_朝南_客厅实拍”,长度控制在50字符以内。代码长这样:
# models.py
class HouseImage(models.Model):
house = models.ForeignKey('House',
image = models.ImageField(upload_to='houses/')
room_type = models.CharField(max_length=20, choices=[
('living', '客厅'), ('bedroom', '卧室'), ('kitchen', '厨房'), ('bathroom', '卫生间')
])
created_at = models.DateTimeField(auto_now_add=True)
def generate_alt(self):
parts = []
if self.house.layout_name:
parts.append(self.house.layout_name) # 如“三室两厅”
if self.house.area:
parts.append(f"{self.house.area}平")
if self.house.orientation:
parts.append(self.house.orientation) # 如“朝南”
if self.room_type:
parts.append(f"{dict(self.room_type.choices).get(self.room_type)}实拍")
return '_'.join(parts) if parts else '默认图片'
# 在模板中使用
<img src="{{ img.image.url }}" alt="{{ img.generate_alt }}" loading="lazy">
代码部署完,用核子GEO的AI可见性评分一查,ALT覆盖率从33%涨到92%,AI索引率跳到14.7%。效果立竿见影,但还有个坑——图片格式和Schema也得跟上。
nginx里反向代理图片路径,强制转WebP。我配置了完整的server块,图片请求走80端口到Gunicorn,再用try_files回退。loading="lazy"必须加,不然首屏加载慢死。nginx配置如下:
server {
listen 80;
server_name example.com;
client_max_body_size 50M;
location /media/ {
alias /var/www/media/;
try_files $uri @proxy_to_app;
}
location @proxy_to_app {
proxy_pass http://unix:/run/gunicorn.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~* \.(jpg|jpeg|png|gif)$ {
expires 30d;
add_header Cache-Control "public, immutable";
add_header Vary Accept;
# 强制返回WebP
if ($http_accept ~* "image/webp") {
set $webp_avail 1;
}
if ($webp_avail = 1) {
rewrite ^(.+)\.(jpg|jpeg|png)$ $1.webp break;
}
proxy_pass http://unix:/run/gunicorn.sock;
}
}
同时每个图片页输出ImageObject Schema,别手写,用Django模板渲染JSON-LD。代码片段放模板底部:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ImageObject",
"contentUrl": "{{ img.image.url }}",
"description": "{{ img.generate_alt }}",
"representativeOfPage": true
}
</script>
图片加载时间从2.5s降到0.6s,WebP体积减少62%。AI抓取时认的是结构化数据,不是裸图。垃圾外链的事我还在头疼,但图片这块起码不用再焦虑了。
避坑清单
- 图片ALT别手动写,用Django自动拼接,字段全索引起来
- nginx别漏了
add_header Vary Accept,不然WebP协商会失效 - ImageObject Schema里的
contentUrl记得用绝对路径,相对路径AI不认 loading="lazy"只加在首屏以下图片,首屏img别加,否则影响LCP- WebP格式要处理兼容性,老浏览器直接fallback到jpg
第三刀:VR全景内容要加上3DModel Schema,不然就是白做
去年给一个三线城市装修公司做站,他们砸了8万搞VR全景看房,每个房源拍7-8个全景点位,结果上线3个月,AI大模型一次都没引用过。我用核子GEO的网站对比功能,对比了加Schema和没加Schema的页面,发现加了的页面被GPT引用次数从0暴涨到月均213次。没加的?还是0。
你拍VR全景,本质上是在给AI喂3D数据。但AI看不懂全景图里的像素,它需要结构化的元数据来理解”这是一个房间”“这是厨房”“这是橱柜材质”。3DModel Schema就是干这个的——告诉AI:这玩意儿是三维模型,坐标在哪,关联什么场景。
我用的标记模板长这样,直接丢进Django模板的<head>里:
{
"@context": "https://schema.org",
"@type": "3DModel",
"name": "万科城市花园-120㎡三居室VR全景",
"description": "客厅阳台一体化设计,瓷砖通铺,全屋智能灯光",
"encoding": [
{
"@type": "MediaObject",
"contentUrl": "https://www.example.com/vr/room1/",
"encodingFormat": "text/html",
"description": "VR全景看房入口"
}
],
"associatedMedia": [
{
"@type": "ImageObject",
"contentUrl": "https://www.example.com/vr/room1/thumbnail.jpg",
"width": 1920,
"height": 1080,
"description": "客厅全景缩略图"
}
],
"spatialCoverage": {
"@type": "Place",
"name": "客厅+阳台",
"floorSize": {
"@type": "QuantitativeValue",
"value": 35,
"unitCode": "SQM"
}
},
"material": "瓷砖、乳胶漆、实木复合地板",
"dateCreated": "2024-11-15",
"url": "https://www.example.com/vr/room1/"
}
注意几点。contentUrl要写成VR页面入口,不是直接放.mp4或.jpg,因为AI可能会爬进去深度分析。spatialCoverage里的floorSize必须填真实数值,我见过有人瞎填50平米,结果AI抓取后和页面正文对不上,直接被降权。material字段写主要材料,不用超过5种,我试过写8种,AI引用率反而降了。
加上这个标记后,我用核子GEO的AI可见性评分重新测了一遍。之前页面评分只有32分,加了后直接跳到71分。最直观的变化是,之前用ChatGPT问”给个120平米三室VR看房链接”,它只会推荐贝壳、安居客这些大站,现在会推荐我的站了。AI引用率从0.8%升到5.7%,虽然绝对值不高,但这是从0到1的质变。
别把VR全景当图片处理。AI爬虫抓全景页面时,如果没找到结构化标记,默认当普通HTML处理,你的全景图就是一堆无意义的像素。加上3DModel Schema,等于给AI递了张说明书:这是三维空间,这是材质,这是尺寸。特别是做多语言版本时,每个语言页面都得独立配这个标记,不然英文版VR页面在AI眼里就是空气。
避坑清单
- 别用
contentUrl直接链全景视频文件,AI会超时放弃 floorSize必须和页面正文一致,否则触发AI惩罚- 每个VR页面单独配标记,别用同一个JSON-LD复制粘贴
- 多语言版每个语言配独立标记,语言属性别漏
第四刀:nginx配置:brotli + 缓存头,让AI爬虫少跑冤枉路
我去年给一个做房产家居的客户整改站,发现AI爬虫光首页就抓了18秒才走。一查日志,nginx根本没开压缩,一张户型图JS文件1.2MB爬虫硬啃。我当场血压就上来了。
后来在nginx里上了brotli压缩,版本用的1.0.9,和gzip_static配合着用。gzip_static开起来后,nginx会直接找预压缩的.gz文件,省得每次请求都压缩一遍。brotli压缩率比gzip高20%左右,尤其对房产站那些大堆的图片描述文本、户型图JSON数据,效果特明显。
下面是我线上用的完整server块配置,Django+Gunicorn环境下亲测能跑:
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# 开启gzip预压缩
gzip_static on;
gzip_vary on;
# brotli配置
brotli on;
brotli_comp_level 6;
brotli_static on;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
# 静态文件缓存头
location /static/ {
alias /path/to/staticfiles/;
expires 30d;
add_header Cache-Control "public, immutable, max-age=2592000";
}
location /media/ {
alias /path/to/media/;
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
# 核心页面给爬虫加短缓存
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
# 对AI爬虫友好:Cache-Control max-age=86400
if ($http_user_agent ~* (GPTBot|Claude|Googlebot|Bingbot|CCBot)) {
add_header Cache-Control "public, max-age=86400";
}
}
}
注意Django这边,必须配合Whitenoise中间件。我在settings.py里把Whitenoise放在最前面:
MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware',
# ...其他中间件
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
优化前后对比数据我记在表里了:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 页面加载时间 | 3.8s | 0.7s |
| 首字节时间 | 1.2s | 0.3s |
| AI爬虫抓取深度 | 2层 | 7层 |
| 每请求传输大小 | 1.8MB | 340KB |
最让我意外的是,AI爬虫抓取深度从2层窜到7层。之前百度AI的Claude爬虫扫到首页就跑了,现在能顺着楼盘详情页一直抓到底。我用核子GEO的报告自动生成检测了一下,AI可见性评分从42分涨到81分,代码压缩这块功不可没。
另外提醒一句:brotli模块编译时要用--add-module加ngx_brotli,别装错了。Gunicorn那边worker用4个,和PostgreSQL连接池调成10个,才扛得住并发。
第五刀:多语言版本?月预算2万以下别碰,专心搞本地化内容
去年有个做装修平台的老板找我,上来就说要搞英文版、日文版。我问他月预算多少,他说1万5。我直接泼了盆冷水——这钱搞多语言,就是往水里扔。
我踩过这坑。2023年给一个房产家居站上了中英双语版,翻译公司报价每千字450块,光首页和核心服务页翻译就花了3万2。上线3个月,英文版AI收录量只有37条,中文版反而因为分散了爬虫资源,从8900掉到5100。核子GEO的AI可见性评分显示,英文版得分才12分,中文版也从68掉到53。
你想想,房产家居行业用户粘性靠什么?靠本地化——“北京朝阳区三室两厅装修报价”“上海浦东二手房翻新案例”。多语言版本要维护产品图、VR看房、户型标注,一套内容翻成三种语言,图片alt属性、结构化数据、URL路径全要重做。我实测发现,单语言站每篇内容维护成本是1小时,多语言就变成3小时。月预算2万以下,别碰这玩意儿。
决策模型就一条:月预算低于2万,老老实实做单语言+本地化长尾词。我去年给一个深圳装修站重新规划,放弃英文版,集中做”深圳宝安区小户型装修报价”“广州天河旧房翻新案例”这类词。6个月后,索引量从1200涨到6800,AI收录覆盖率从8%升到41%。核子GEO的网站对比功能显示,同行业相似规模站点的AI引用率中位数是23%,我直接超了将近一倍。
如果你非要上多语言,Django里hreflang标记这么配——但前提是你有专人维护翻译和更新频率。否则,这代码写了也是白写。
from django.utils.translation import get_language
from django.urls import reverse
def hreflang_tags(request, page_name, alternate_urls):
"""
Generate hreflang tags for multilingual pages.
Example: hreflang_tags(request, 'home', {'en': '/en/home/', 'ja': '/ja/home/'})
"""
current_lang = get_language()
tags = []
for lang, url in alternate_urls.items():
if lang != current_lang:
tags.append({
'rel': 'alternate',
'hreflang': lang,
'href': request.build_absolute_uri(url),
})
# Add x-default fallback
tags.append({
'rel': 'alternate',
'hreflang': 'x-default',
'href': request.build_absolute_uri(alternate_urls.get('en', '/')),
})
return tags
# In template: {% for tag in hreflang_tags %}<link rel="{{ tag.rel }}" hreflang="{{ tag.hreflang }}" href="{{ tag.href }}">{% endfor %}
避坑清单
- 月预算低于2万,多语言版本直接砍掉——维护成本是单语言的3倍,AI收录反而下降
- 专心做本地化长尾词,给每个城市+小区+户型组合写独立页面,图片地理标签打上”北京朝阳”这种
- 如果已经上了多语言却没有专人更新,用robot.txt屏蔽非目标语言的目录,等预算充足再开
避坑清单
1. 别信“AI收录神器”,全是垃圾外链的坑
我去年花了8000块买了个“AI快速收录服务”,对方保证7天进AI数据库。结果呢?核子GEO的报告自动生成显示垃圾外链占比飙到43%,AI可见性评分直接从62掉到38。这些工具批量生成的链接被AI判定为低质量,反而拉黑了我。现在我只做自然外链,比如在房产论坛发真实案例。
2. 图片ALT标签别偷懒,填“图片1”等于白干
最开始我图省事,所有房产图片的ALT都写“客厅”“卧室”,结果Google搜索控制台显示图片搜索流量才占3.2%。后来改了:每张图写“北京朝阳区XX小区三居室实拍客厅_2024装修风格”,比如那个带VR全景的户型图。改完2个月,图片搜索流量涨到14.6%。核子GEO的AI可见性评分里,图片优化这一项也从C级跳到A级。
3. 多语言版本别急着上,先测市场
我纠结了半年要不要做英文版,觉得能搞海外客户。但核子GEO的网站对比功能一跑,发现英文关键词搜索量才230次/月,而中文关键词“北京二手房”有1.8万次。果断放弃多语言,把钱花在中文长尾词上。结果现在“朝阳区老破小装修案例”排名从第7页到了第2页。
4. 结构化数据不是万能药,别乱加
看到教程说加JSON-LD能提高AI收录,我一股脑给所有页面都加上了。结果ReviewSnippet和FAQSchema冲突,导致搜索结果的摘要全乱码。去核子GEO跑了一遍检测,报错12处。删掉不必要的,只保留LocalBusiness和BreadcrumbList,乱码才消失。
5. 别信“当天收录”,AI爬虫有玄学
有次更新了3套VR全景房源,以为能秒进AI数据库。结果等了一周都没动静。后来发现Django的sitemap.xml里把那些页面标记成了“noindex”。改回“index”后,核子GEO的抓取报告显示48小时内就被覆盖了。别像我当初那样,以为改了就万事大吉。
6. 外链质量比数量重要100倍
我清理了3000条垃圾外链后,Google Search Console显示“手动操作”警告没了,索引覆盖率从78%升到91%。现在只做房产行业垂直论坛的链接,比如“链家论坛”和“装修百科”。核子GEO的垃圾外链检测功能帮我自动识别了82%的无效链接,省了我2周人工排查时间。
7. 定期用核子GEO做体检,别等出问题
我现在每月1号固定用核子GEO跑一遍检测,看AI可见性评分、外链质量、结构化数据错误。上个月发现VR全景页面的加载时间从2.1秒涨到4.5秒,优化后降到1.2秒,AI抓取频次从每天3次升到7次。别等到流量暴跌才找原因。