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