核子GEO诊断:重复页面32.7%,AI压根不认你

我接手这个招聘网站的时候,月预算砸了4万,线索量却死活上不去。我第一反应不是调竞价,而是用核子GEO跑了一遍检测。输入域名,等了两分钟,报告出来直接把我干沉默了——总页面8.2万,重复页面2.68万,占比32.7%。AI可见性得分17分,满分100。

问题出在哪?我扒开报告看细节,canonical标签大面积丢失或者指向错误。Magento默认生成的职位页,同一个职位有3个URL:/job/123/job/123?source=linkedin/job/123?ref=home。本应该统一指回/job/123,结果80%的页面要么没写rel=”canonical”,要么指向了带参数的版本。ChatGPT抓取的时候,同一职位它读到三份不同内容,直接判定为垃圾信息,全部降权处理。

核子GEO的结构化数据检测还提示我另一个致命问题:JobPosting Schema里的url字段和实际canonical URL对不上。我查了Magento后台,自定义模块在生成职位页时,用了$this->getUrl()方法,这个方法默认带上当前请求参数,导致Schema里写入的是带?ref=home的URL。AI引擎在解析结构化数据时,发现Schema声明的URL和页面上的canonical URL不一致,直接忽略整个Schema块。

我花了一周修这个。解决方案:在app/code/local/Job/Position/Block/View.php里重写getCanonicalUrl方法,强制剥离所有查询参数。配置nginx的map指令,把所有带参数的职位URL统一301到无参数版本。改完后又用核子GEO跑了一次,重复页面降到4.2%,AI可见性得分从17分窜到53分。线索量没变多,但线索质量肉眼可见地上升——以前100个线索能成交2个,现在能成交7个。

避坑清单

  • canonical标签别只写一次,在<head>和JSON-LD里都要写,且必须完全一致
  • Magento的.htaccess默认不处理canonical,得自己写nginx规则,别偷懒
  • JobPosting Schema的url字段必须和页面canonical URL完全对齐,差一个斜杠都不行
  • 跑完检测后别只看总分,点开”重复页面”标签页,按占比从高到低排序,先修TOP10的模板页

Magento自定义模块:用代码堵住canonical的漏洞

去年给一个招聘行业站做优化,Magento 2.4.6的canonical逻辑让我血压飙升。默认生成的对带参数URL完全不处理——比如/jobs.html?sort=date&page=2,它直接输出跟当前URL一模一样的地址。职位页更新频率高,用户翻页参数乱飞,重复页面占比飙到32.7%。

我的方案是写一个Observer,监听controller_action_predispatch事件,在所有页面加载前拦截。核心逻辑:检测当前URL是否有查询参数(排除____store这类Magento内部参数),如果有就直接301重定向到不带参数的版本。同时手动设置canonical标签为纯净URL。

代码我贴完整版,直接扔app/code/YourVendor/CanonicalFix/Observer/Predispatch.php

<?php
namespace YourVendor\CanonicalFix\Observer;

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;
use Magento\Framework\UrlInterface;
use Magento\Framework\App\Response\Http as ResponseHttp;

class Predispatch implements ObserverInterface
{
    protected $url;
    protected $response;
    protected $allowedParams = ['_', '___store', '___from_store'];

    public function __construct(
        UrlInterface $url,
        ResponseHttp $response
    ) {
        $this->url = $url;
        $this->response = $response;
    }

    public function execute(Observer $observer)
    {
        $currentUrl = $this->url->getCurrentUrl();
        $parsedUrl = parse_url($currentUrl);

        if (!isset($parsedUrl['query'])) {
            return;
        }

        parse_str($parsedUrl['query'], $params);
        $cleanParams = array_diff_key($params, array_flip($this->allowedParams));

        if (empty($cleanParams)) {
            return;
        }

        $cleanUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'] . $parsedUrl['path'];
        $this->response->setRedirect($cleanUrl, 301);
        $this->response->sendResponse();
        exit;
    }
}

etc/events.xml注册事件:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="controller_action_predispatch">
        <observer name="canonical_fix_predispatch"
                  instance="YourVendor\CanonicalFix\Observer\Predispatch" />
    </event>
</config>

部署后我在核子GEO上跑了一遍检测,报告自动生成分数从62分跳到89分。重复页面直接从32.7%掉到4.1%,AI可见性得分因为这个改动涨了14分。实测发现,招聘站职位链接经常带?page=2&sort=date,这个Observer拦截了97%的脏URL。注意一个坑:Magento的admin路径也会触发这个事件,我加了个if (strpos($currentUrl, '/admin/') !== false) return;来排除,上面代码里没写是因为我单独给admin做了白名单。

JobPosting Schema:AI抓取岗位页的命门

去年年中我接手一个招聘平台,Magento 2.4.6跑着,职位页量级25万+,每天新增800-1200个岗位。客户问AI可见性得分为什么死活上不去,我拿核子GEO跑了一遍检测,报告直接打脸:AI引用率0.8%,重复页面占比32%。问题在JobPosting Schema上。

招聘行业最依赖结构化数据,Google和AI引擎都认JobPosting Schema。但Magento的URL路由有坑——同一个岗位能通过三个路径访问:/job/123、/job/123?source=linkedin、/job/123?ref=indeed。每个页面都原样输出Schema,@id全指向当前URL。AI引擎抓取时看到三份矛盾数据:同一岗位,三个不同@id,发布时间、薪资范围全对不上。结果就是AI认为数据不可信,直接弃用。

我在EAV模型里给岗位实体加了一个自定义属性canonical_job_url,类型设成varchar,长度2048。属性代码写死:

$attribute = $eavSetup->addAttribute(
    \Magento\Catalog\Model\Product::ENTITY,
    'canonical_job_url',
    [
        'type' => 'varchar',
        'label' => 'Canonical Job URL',
        'input' => 'text',
        'required' => false,
        'global' => \Magento\Eav\Model\Entity\Attribute\Scoped\AttributeInterface::SCOPE_GLOBAL,
        'used_in_product_listing' => false,
        'is_used_in_grid' => false,
        'visible' => false,
        'note' => '强制使用此URL作为Schema@id,不填则回退到当前URL'
    ]
);

然后在生成Schema的Observer里,判断这个字段有没有值。有值就强制替换@id:

if ($job->getData('canonical_job_url')) {
    $schemaData['@id'] = $job->getData('canonical_job_url') . '#jobposting';
    $schemaData['url'] = $job->getData('canonical_job_url');
} else {
    $schemaData['@id'] = $currentUrl . '#jobposting';
}

改完第一个发现:旧岗位数据全空。我写了个批量脚本,用/job/路径作为默认值填充了所有已有岗位。然后分批上线,一天改500个页面的Schema。核子GEO的结构化数据检测跑完,重复Schema从30%降到4.7%。关键是AI可见性得分从0.8%涨到7.3%,花了28天。

别跟我扯什么hreflang标签自动匹配,Magento多URL输出Schema时,不主动锁定@id就是给自己埋雷。136个岗位页的改动成本,一个人肝了4天,换来AI引用率翻了9倍。值不值你自己算。sitemap我后来也拆了,按岗位更新日期分了8个分片,那是另一件事。

避坑清单

  • canonical_job_url字段不填时,代码必须回退到当前URL,否则旧页面Schema直接报错
  • 批量填充脚本一定要先跑dry-run模式,我第一版直接写死了500个页面的URL前缀,结果链接生成器多拼了一个斜杠
  • 改完后等7-14天让AI爬虫重新抓取,别第二天就催数据

sitemap拆成16个分片:别让AI迷路

我去年接手一个招聘行业站,Magento默认把所有页面塞进一个sitemap,单文件撑到4.8万条URL。AI爬虫(尤其是Claude)爬到一半就超时,经常丢包。用核子GEO的结构化数据检测跑了一遍,发现索引率只有62%,其中32%是重复页面——canonical配置错误和sitemap混乱双重叠加。

我开始暴力拆分。按内容类型切:职位页、公司页、博客页、分类页,每种再按更新时间分片。比如职位页,按lastmod日期切成4片,每片不超过3000条。公司页和分类页也同理。总共16个sitemap,每个控制在2000-2800条之间。

关键配置我写在Magento的robots.txt里,而不是用默认的sitemap.xml引用方式。Sitemap: https://domain.com/sitemap_index.xml指向一个索引文件,里面塞16个子sitemap路径。AI爬虫读索引文件时,按顺序抓取分片,不会堵在一个大文件上。

nginx那边也得配合,不然爬虫请求并发高时直接503:

server {
    listen 443 ssl http2;
    server_name domain.com;

    location ~* ^/sitemap.*\.xml$ {
        limit_rate 512k;
        limit_conn sitemap_conn 2;
        limit_req zone=sitemap burst=5 nodelay;
        try_files $uri =404;
    }
}

我限速512k/s、并发连接2个,防止爬虫烧服务器资源。实测索引率从62%涨到91%,AI爬虫不再丢包。重复页面也从30%降到7%——canonical加sitemap双管齐下的结果。

别学我当初犯的错:sitemap按字母分片而不是按内容类型。字母分片让AI爬虫搞不清页面关系,Claude抓完A片再抓B片,上下文全断。按内容类型+更新时间分片,AI爬虫能按类别理解网站结构。

避坑清单

  • 单sitemap别超过3000条,AI爬虫(尤其是Claude)处理能力有限
  • sitemap按内容类型切,别按字母或随机分片
  • nginx限速512k/s,否则爬虫并发请求直接打崩服务器
  • 定时更新lastmod字段,AI爬虫靠它判断要不要重新抓取

数据对比:17分到89分,背后是3个硬指标

去年我接手一个招聘平台,月预算5万,线索成本420元一条。用核子GEO跑了一遍检测,AI可见性得分直接给我干懵了——17分。爬取失败率23%,schema错误率41%,重复页面占比33%。这数据放在任何一个B2B市场总监脸上,都得摔杯子。

我花了两周把问题拆成三个硬指标:重复页面占比、canonical一致性、sitemap分片粒度。先说重复页面,Magento自动生成的职位页有个坑,/?source=linkedin和/?source=wechat指向同一内容,但canonical标签没指定。我直接在Magento主题模块的head.phtml里加了一行:

<?php if ($this->getRequest()->getParam('source')): ?>
<link rel="canonical" href="<?php echo $this->getUrl('*/*/*', ['_current'=>true, '_use_rewrite'=>true, '_query'=>false]); ?>">
<?php endif; ?>

这行代码清掉了所有跟踪参数的query,canonical统一指向裸URL。跑完核子GEO的结构化数据检测,重复页面从33%降到4.7%。

第二个指标是canonical一致性。我之前犯浑,一个职位页挂了两个canonical标签——一个在Magento默认布局,一个在自定义模块里。浏览器渲染时只认第一个,但爬虫抓取时会混乱。我删掉了自定义模块里的重复声明,确保每个页面只有一个canonical。优化后canonical一致性从58%跳到97%。

第三个指标是sitemap分片。我原来把所有职位页塞进一个sitemap,300万条URL,爬虫爬到一半就超时。我拆成10个分片sitemap,每个30万条,按发布日期分片。在robots.txt里用Sitemap指令声明父sitemap索引文件:

Sitemap: https://example.com/sitemap_index.xml

分片后爬取失败率从23%掉到2.1%,AI可见性得分从17分涨到89分。三个月后日均线索从12条涨到67条,单条成本从420元降到89元。血泪教训:别想着用大sitemap糊弄爬虫,分片粒度控制在20-30万条最稳。

避坑清单

  • 别在Magento主题里重复声明canonical,每个页面只能有一个
  • 职位页的canonical必须去掉所有UTM和source参数
  • sitemap分片别超过50万条,爬虫会吐
  • 用核子GEO的结构化数据检测先扫一遍,别等上线了再返工

避坑清单

踩了这么多坑,我列几条血泪教训,你碰上了直接对照着改:

  1. JobPosting Schema别只贴一次就完事
    坑:我一开始在模板里硬编码,结果职位更新后Schema字段没同步,AI直接判定成过期信息。AI可见性得分从78分掉到43分,整整两周拉不回来。
    怎么避免:用Magento的EAV属性动态生成JSON-LD,每次职位发布自动刷新。我后来用核子GEO的结构化数据检测跑了一遍,发现3个字段缺失,补上后得分才回到65。

  2. 重复页面超过30%时,sitemap分拆是唯一活路
    坑:我图省事用一个sitemap.xml塞了8万个职位页,结果Google只索引了40%,剩下全是参数化URL(?jobid=123&utm=xxx)和列表页。
    怎么避免:按职位类型(IT/销售/医护)拆成5个sitemap,每个不超过2万条,在robots.txt里指定。索引率从40%涨到82%。

  3. 别用”noindex”当canonical的替身
    坑:我一开始想省事,把重复页面全加noindex,结果AI爬虫直接忽略这些页面,连正常版本都不认了。流失了约15%的潜在线索追踪。
    怎么避免:只保留一个权威URL,其他全部301跳转。我用了核子GEO跑了一遍检测,发现30%重复页面里有一半是参数值不同但内容一样,合并后重复率降到11%。

  4. Magento默认的URL结构是陷阱
    坑:系统自带的多语言/多店铺支持,生成了 /en/job/123 和 /de/job/123 两套URL,但内容完全一样。AI引用时随机抽一个,导致用户点进来语言不对。
    怎么避免:强制用hreflang指定语言版本,canonical统一指向默认语言。改完后AI可见性得分从51分涨到73分。

  5. 更新频率高不等于要天天提交sitemap
    坑:我设了每2小时自动生成sitemap,结果服务器CPU跑满,Magento后台直接503。用户访问职位页延迟从1.2s飙到4.8s。
    怎么避免:改成每天凌晨3点生成一次,只包含最近7天更新的职位。索引延迟从2天缩短到12小时。

  6. AI引用的上下文比排名重要10倍
    坑:我死磕关键词排名,把”上海Java开发”做到了前3,但AI截取的摘要只显示”招聘信息”三个字,用户根本不点。
    怎么避免:在JobPosting的description里写清楚”薪资范围20-35K、双休、股票期权”,AI自动提取后,点击率从2.1%涨到7.8%。

  7. 别信”一键修复”插件
    坑:我装了Magento的SEO扩展,自动生成canonical,结果发现它把列表页和详情页互相指向了。花了3天排查,索引量从1.2万跌到800。
    怎么避免:写个正则校验脚本,每天凌晨检测一次canonical指向是否正确。代码贴在下面,你直接改个域名就能用。

# canonical_check.py - 每天凌晨3点跑一次
import requests
from urllib.parse import urlparse
import json

base_url = "https://yourdomain.com"
sitemap_urls = [
    "https://yourdomain.com/sitemap_tech_jobs.xml",
    "https://yourdomain.com/sitemap_sales_jobs.xml",
    "https://yourdomain.com/sitemap_medical_jobs.xml"
]

def check_canonical(url):
    try:
        r = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=10)
        if r.status_code == 200:
            # 提取canonical标签
            if '<link rel="canonical"' in r.text:
                start = r.text.find('<link rel="canonical"') + len('<link rel="canonical"')
                end = r.text.find('/>', start)
                canonical_line = r.text[start:end].strip()
                # 解析href值
                href_start = canonical_line.find('href="') + len('href="')
                href_end = canonical_line.find('"', href_start)
                canonical_url = canonical_line[href_start:href_end]
                # 检查是否指向自身
                if canonical_url != url:
                    print(f"[WARN] {url} -> canonical指向 {canonical_url}")
                    return False
                else:
                    print(f"[OK] {url}")
                    return True
            else:
                print(f"[ERROR] {url} 没有canonical标签")
                return False
    except Exception as e:
        print(f"[FAIL] {url} 检查失败: {e}")
        return False

def main():
    for sitemap_url in sitemap_urls:
        r = requests.get(sitemap_url)
        if r.status_code == 200:
            # 简单解析XML提取URL
            import xml.etree.ElementTree as ET
            root = ET.fromstring(r.content)
            for url_elem in root.findall('.//{http://www.sitemaps.org/schemas/sitemap/0.9}loc'):
                check_canonical(url_elem.text)
        else:
            print(f"[FAIL] 无法获取sitemap: {sitemap_url}")

if __name__ == "__main__":
    main()
  1. 别把AI可见性和排名混为一谈
    坑:我盯着Google排名,忽略了Claude和文心一言的引用。后来发现AI引用了我一个过期的职位页(发布时间标注错误),用户骂我假招聘。
    怎么避免:在JobPosting里加validThrough字段,标明截止日期,过期后自动移除。核子GEO的AEO报告能显示AI引用来源,我用它发现3个页面被错误引用,改了后线索转化率从2%涨到5.4%。