搜索全站
主题 12 分钟阅读

主题与插件结构化数据重复输出:定位与去重处理

页面里有两份 JSON-LD,Google 报「检测到重复字段」?本文讲清结构化数据的三类来源,给出定位命令、关闭主题或插件一方输出的具体代码,以及去重后的验证方法。

U
UFO_PF 2023-05-24 · 阅读 12 分钟
主题 主题
阅读提示
本文约 4,663 字,预计阅读 12 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:富媒体测试工具报一堆「重复字段」

把文章链接丢进 Google 的富媒体结果测试工具,本想确认星级评分能不能显示,结果看到一屏警告:「检测到重复的字段」「同一页面存在多个 Article 类型」「缺少 author」。点开详情发现页面里赫然有两份 JSON-LD,一份来自主题,一份来自 SEO 插件,内容还不一样——主题那份的作者是「admin」,插件那份是真实作者。

结构化数据重复不会让页面报错,也不会影响前台显示,所以极易被忽略。但它的后果很实在:Google 无法判断哪一份是准的,可能整体放弃解析,富媒体展示(星级、面包屑、FAQ 折叠)随之失效。本文讲清这些 JSON-LD 从哪来、怎么用一条命令数出来、以及关掉哪一方更合理。

原理分析:结构化数据的三类来源

WordPress 页面里的结构化数据通常有三个来源,各自走不同的输出路径:

来源 输出位置 典型内容 可控性
SEO 插件 wp_head(Yoast)或 wp_footer WebSite、WebPage、Article、Breadcrumb、Organization 高,有过滤器
主题/子主题 wp_head 或 wp_footer,有的写在模板里 Article、Product、FAQ、LocalBusiness 中,需找钩子
其他插件 各自挂载 评论评分、商品、活动 中

Yoast 的 schema 输出走一套「图谱(graph)」机制:它把 WebSite、WebPage、Article、Breadcrumb 等碎片组装成 @graph 数组,开发者可以用 wpseo_schema_graph_pieces 增删碎片。Rank Math 则用 rank_math/json_ld 过滤器对整个 JSON-LD 数组做最后修改。

主题侧的写法五花八门,常见的有:在 header.php 里直接 echo '<script type="application/ld+json">';用 wp_footer 挂载;或者在 functions.php 里写死一段。第三类最难处理,因为它不挂钩子,只能找到那行代码注释掉(务必在子主题里覆盖,不要改父主题)。

定位方法只有一条可靠的:把页面 HTML 抓下来数 application/ld+json 出现的次数。浏览器开发者工具里也能搜,但容易漏掉 JS 动态注入的部分。

保姆级实操

  1. 数出份数:用 curl 抓取页面,统计 application/ld+json 的次数,并把每段内容存下来看 @type。
  2. 判断责任方:看 JSON-LD 里的 @type 与字段特征。带 @graph 的基本是 Yoast;带 rank-math 字样的是 Rank Math;主题自己写的通常结构简单、字段少。也可以在 HTML 里看这段脚本前后的注释。
  3. 优先关主题那一方:理由和 SEO 设置一样——插件的 schema 更完整、可维护、能随插件升级跟进 Google 的规则变化。主题选项里若有「结构化数据 / Rich Snippet」开关,直接关。
  4. 主题没开关就找钩子:在主题里搜索 ld+json,找到挂载的 action 名与函数名,用 remove_action 摘掉。
  5. 插件侧也要按需裁剪:如果插件输出的碎片里有你不需要的类型(比如站点没有作者信息却输出 Person),用 wpseo_schema_graph_pieces 移除。
  6. 验证:清缓存后重新抓一次,确认只剩一份;再跑一次富媒体结果测试工具,确认警告清零。
# 数一数页面里有几份 JSON-LD
curl -s https://example.com/sample-post/ | grep -c 'application/ld+json'

# 把每段 JSON-LD 抽出来看 @type
curl -s https://example.com/sample-post/ \
  | grep -o '<script type="application/ld+json"[^>]*>.*</script>' \
  | grep -o '"@type"[^,]*' | sort | uniq -c

# 看是谁输出的:脚本前后的注释常带来源
curl -s https://example.com/sample-post/ | grep -B2 'application/ld+json'
<?php
// 摘掉主题自己输出的结构化数据(钩子名需在主题里确认)
remove_action( 'wp_head', 'theme_output_json_ld', 10 );
remove_action( 'wp_footer', 'theme_output_json_ld', 10 );
<?php
// Yoast:按需裁剪 schema 图谱里的碎片
add_filter( 'wpseo_schema_graph_pieces', function( $pieces, $context ) {
    // 移除不需要的碎片,保留 Article / WebPage / Breadcrumb
    $remove = array( 'person', 'organization' );
    foreach ( $remove as $type ) {
        if ( isset( $pieces[ $type ] ) ) {
            unset( $pieces[ $type ] );
        }
    }
    return $pieces;
}, 11, 2 );

// 修正 Article 碎片里的字段
add_filter( 'wpseo_schema_article', function( $data ) {
    if ( isset( $data['datePublished'] ) ) {
        $data['dateModified'] = get_the_modified_date( 'c' );
    }
    return $data;
} );

// 极端情况:彻底关闭 Yoast 的 schema(只在完全自己输出时使用)
// add_filter( 'wpseo_schema_graph_pieces', '__return_empty_array' );
<?php
// Rank Math:统一改写 JSON-LD
add_filter( 'rank_math/json_ld', function( $data, $jsonld ) {
    if ( isset( $data['richSnippet'] ) && empty( $data['richSnippet']['author'] ) ) {
        unset( $data['richSnippet'] );
    }
    return $data;
}, 10, 2 );
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "文章标题",
  "datePublished": "2026-10-01T09:00:00+08:00",
  "dateModified": "2026-10-09T14:30:00+08:00",
  "author": {
    "@type": "Person",
    "name": "作者名"
  },
  "publisher": {
    "@type": "Organization",
    "name": "站点名",
    "logo": {
      "@type": "ImageObject",
      "url": "https://example.com/logo.png"
    }
  },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://example.com/sample-post/"
  }
}

常见踩坑

  • 只数 curl 输出不看缓存:开了页面缓存时抓到的是缓存副本,改完必须先清缓存再抓。
  • 两份 schema 的 @id 不一致:Google 会认为是两个不同实体,比字段重复更麻烦。统一 @id 后再验证。
  • 主题把 JSON-LD 写死在模板里:remove_action 无效,只能复制模板到子主题再删除那段。复制模板的原则见 子主题模板覆盖规范。
  • 用 __return_empty_array 彻底关掉插件 schema:页面将完全没有任何结构化数据,富媒体展示全丢。除非你自己输出,否则别这么干。
  • wpseo_schema_graph_pieces 优先级太低:Yoast 自己用 10,你的过滤器必须用 11 以上才能生效。
  • 案例:一个本地生活站点在 Search Console 里长期收到「缺少字段 author」的提示,运营以为是插件没配好,反复检查 Yoast 设置无果。用 curl 一数,页面里有三份 JSON-LD:主题的 LocalBusiness、Yoast 的图谱、还有一个评论插件的 AggregateRating。Google 解析时把三份混在一起,author 字段被主题那份的空值覆盖。最终关掉主题的 LocalBusiness、保留 Yoast 图谱并补上作者信息,警告两周内降为零。多份 schema 不会「取并集」,只会互相干扰。
  • 改完想验证却不知道去哪验:校验工具与修复方法见 主题 schema 微数据检查方法。

总结

处理重复结构化数据的顺序是:数份数 → 看 @type 判断责任方 → 关掉主题那一方 → 按需裁剪插件碎片 → 清缓存后重新验证。取舍原则与 SEO 设置一致:留给专业插件,主题侧关闭。整个页面最终应只剩一份来源清晰、字段完整的 JSON-LD。注意插件之间也可能冲突(评论插件、商城插件都会输出 schema),上线前务必全站抽查首页、文章页、产品页三类页面各一次。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。