商品页的结构化数据里,一半找不到哪个节点是这一页
本文目录
- 一页摆着几十个Product,机器怎么知道哪个是这一页?
- 按完整地址算,有多少页真的认领了自己?
- 四个站一页都指认不出来
- 第一版算出来的数,为什么高了40个点?
- @id这一栏,53个站写了多少?
- 把各站的@id摆在一起看,写法有五种
- 有一个站的两个节点用了同一张身份证
- mainEntityOfPage和WebPage节点,有几个站写了?
- 结构化数据里的商品名和页面上的h1,对得上吗?
- 一个商品页应该有几个h1?
- canonical做对了,为什么JSON-LD这一层没有?
- 第一,canonical有过一次全行业的疼
- 第二,检查工具不查这一项
- 第三,主体指认的收益是延迟的
- 自己的商品页怎么补上这个身份?
- 第一步:先测一遍现状
- 第二步:给主商品节点写上三样东西
- 第三步:给变体节点一个不同的标识
- 第四步:把这一项写进上线检查
- 顺带把url这一栏也理一遍
- 什么时候不必做
- 常见问题解答
- 不写@id,商品富媒体结果会受影响吗?
- @id应该填什么值?
- mainEntityOfPage和WebPage的mainEntity,选哪个写?
- 变体节点也要写@id吗?
- 页面上有多个h1,需要改吗?
- 怎么快速判断自己站上有没有这个问题?
- 这件事和canonical有冲突吗?
- 权威参考资料
摘要:53个英文独立站的392个商品页,把每一页的JSON-LD节点全取下来,问一个很朴素的问题——这一堆Product节点里,哪一个是这一页正在卖的东西?按完整地址严格比对,369个有商品节点的页面里只有188页能给出确定答案,占50.9%。2700个Product节点中,写了@id的占44.2%,写了mainEntityOfPage的只有0.9%;有5个节点的@id干脆就是一个井号。同一批页面的canonical标签写了96.8%、指向自己的有94.9%——同一件事,HTML那一层早就有了约定,结构化数据这一层还没有。
一页摆着几十个Product,机器怎么知道哪个是这一页?
上一篇数节点的时候发现一个绕不过去的问题:既然一个商品页动辄写几十个Product节点,那么当搜索引擎或者购物代理读到这一页,它凭什么知道这几十个里面哪一个是这一页在卖的东西?
这个问题在HTML那一层早就有答案了。link rel="canonical"就是干这个的——这一页的规范地址是谁,写清楚,一行搞定。本批392个页面里,96.8%写了canonical,其中94.9%指向自己。这一层的普及率高到几乎可以当成默认行为。
但JSON-LD那一层没有一个同样普及的写法。它有几个候选:给节点写@id,给节点写url,或者写mainEntityOfPage把节点和页面绑起来。这三样都是标准里有的,问题是有多少人用。
所以这一篇只做一件事:逐页问一遍,这一页的结构化数据能不能指出它自己。
按完整地址算,有多少页真的认领了自己?
判据是这样定的:把每个Product节点上的url和@id取出来,补全成绝对地址,跟这个页面自己的地址逐字比对。对上了,就算这个节点认领了本页。
| 结果 | 页数 | 占比 |
|---|---|---|
| 正好一个节点对上——能确定主体 | 188 | 50.9% |
| 多个节点同时对上——有歧义 | 30 | 8.1% |
| 一个都对不上——机器只能猜 | 151 | 41.0% |
样本是369个至少写了一个Product节点的页面。能给出确定答案的刚过一半。
要说清楚的是,对不上不等于标记失效。多数情况下机器还是能猜对——一页只有一个Product节点的时候,那就是它,不需要指认。本批有近六成的页面属于这种情况,简单页面天然没有歧义。
麻烦的是另一批。当一页有几十个Product节点、而没有一个用地址认领自己的时候,机器只能靠位置和上下文去推:写在最外层的那个大概是主体,包在hasVariant里的大概是它的成员。这个推断多数时候成立,但它是推断,不是声明。
Google结构化数据的入门文档里有一句常被略过的话:标记必须描述这一页上的内容。这句话默认了一个前提——读的人知道哪部分是这一页的内容。当一页交出去四十个商品节点,这个前提就得由页面自己来保证,而多数页面没有保证它。
四个站一页都指认不出来
framebridge、glossier、italic、wusthof这四个站,抽到的页面里没有任何一页的Product节点带上了能对上本页地址的url或@id。它们不是没写标记——framebridge八个页面全都有完整的Product加Offer加Brand,字段该有的都有,就是没有一处写明这份标记描述的是哪个网址。
这四个站的商品页都只有一个Product节点,所以实际影响很小。但它反映出一件事:写标记的时候,多数人想的是把这件商品的属性填全,没想过要说清这份标记属于哪一页。这跟规格字段那次实测看到的思路是一样的——大家都在按字段清单打勾,清单上没有的项目,就不会有人想起来。
第一版算出来的数,为什么高了40个点?
这一节讲我自己差点写错的地方。
第一次跑这个统计的时候,我在浏览器里做地址比对,图省事写了个归一化函数:取协议、域名和路径,把查询参数和末尾斜杠都扔掉,再比。这么算出来的结果漂亮多了——91.3%的页面至少有一个节点对上,只有8.7%完全对不上。
然后我去翻具体案例,发现不对劲。
untuckit的一个页面有29个Product节点,按归一化口径,其中十几个都对上了本页地址。点开看才明白:这些变体节点的url全都是同一个商品路径加不同的?variant=参数,参数被我扔掉之后,它们当然全部相等。
| 口径 | 至少一个对上 | 多个对上 | 说明 |
|---|---|---|---|
| 忽略查询参数 | 91.3% | 34.4% | 变体参数被抹平,全算成同一个地址 |
| 完整地址逐字比对 | 59.0% | 8.1% | 只认真正指向本页的那一个 |
两个口径差了32个百分点,而它们回答的根本不是同一个问题。忽略参数那一版回答的是这堆节点是不是都在这个商品路径底下,答案当然是;完整比对那一版回答的才是有没有一个节点声明自己就是这一页。
这个坑跟从站点地图里找重复网址那次踩的是同一类:归一化是为了消除噪声,可一旦被消掉的恰好是承载区分信息的那部分,噪声没了,信号也没了。那次是把网址里的数字当噪声删掉,结果把5寸和7寸的厨刀算成了重复;这次是把查询参数当噪声删掉,结果把三十个变体算成了三十次自我认领。
两次的共同点还有一个:错的那一版,读起来都比正确的那一版更顺眼。91.3%是个让人放心的数字,看到它不会有人追问;50.9%才会让人想去核对。保哥现在给自己定的规矩是,凡是归一化过的字段,出结论之前必须拿原值再跑一遍,两个数差得越大,越要先解释差在哪儿,而不是挑好看的那个。
@id这一栏,53个站写了多少?
2700个Product节点,逐个看它们身上的四个字段:
| 字段 | 写了的节点 | 占比 |
|---|---|---|
| offers | 2344 | 86.8% |
| url | 1537 | 56.9% |
| @id | 1193 | 44.2% |
| mainEntityOfPage | 24 | 0.9% |
报价字段的填写率很高,这符合预期——它直接关系到价格能不能出现在搜索结果里,有人盯。身份类的三个字段一路往下掉,掉到最后一个只剩0.9%。
把写了@id的那批再摊开看取值形态,情况更具体:
| @id的写法 | 取样节点数 | 后果 |
|---|---|---|
| 完整的绝对网址 | 461 | 正确用法 |
| 相对路径 | 348 | 解析方需要自己拼基地址,跨文档引用会失效 |
| 页内锚点,如#product | 2 | 只在本页内唯一,出了这一页认不出 |
| 就一个井号 | 5 | 等于没写 |
相对路径那348个值得单说。schema.org的数据模型说明里,@id的作用是给这个节点一个全局唯一的标识,好让别的文档、别的数据源能引用同一个实体。写成相对路径的时候,这个标识就只在当前文档里成立了,跨文档那一半功能直接没了。
至于那5个只写了一个井号的——它们出现在allbirds的商品页上,每一页的主商品节点都是"@id": "#"。这个值语法合法、校验器不报错、富媒体测试也能过,唯一的问题是它谁也标识不了。
把各站的@id摆在一起看,写法有五种
同一个字段,53个站写出了五种互不兼容的形态:
| 写法 | 样例 | 能不能跨文档引用 |
|---|---|---|
| 绝对地址加语义锚点 | untuckit的商品地址后面缀#json-ld-for-seo | 可以,且能区分同页多个实体 |
| 绝对地址加变体参数 | stanley的商品地址后面缀?variant=加编号 | 可以,变体之间也分得开 |
| 纯绝对地址 | avocadogreenmattress直接用商品页地址 | 可以,但同页只能有一个实体用 |
| 相对路径加锚点 | ritual的/products/xxx#product | 不行,出了这份文档就失效 |
| 一个井号 | allbirds的# | 不行,等于没写 |
前三种都是对的,只是详略不同。要紧的是后两种:它们看起来像是写了,在任何一份统计填写率的报告里都会被算成合格。如果你只统计这一栏填没填,53个站的@id覆盖率是44.2%;如果统计的是填了之后能不能用,要再砍掉三成。
这跟品牌字段那次实测得到的是同一个结论:一个字段的填写率和它的可用性,是两个数。区别在于品牌栏至少还有人肉眼看得懂对错,@id写成一个井号,连看的人都不会觉得有问题。
有一个站的两个节点用了同一张身份证
ritual.com抽到的7个页面,每一页都有同样的毛病:页面上有两个Product节点,两个的@id写的是同一个值,形如商品路径加#product。
这件事的后果比看起来严重。@id在这套数据模型里的含义是节点标识,两个节点写了同一个@id,规范上它们就是同一个节点——解析方会把两份属性合并成一份,同名属性谁覆盖谁,取决于实现。
换句话说,ritual想描述的是两样东西,交出去的是一样东西,而合并之后剩下哪些属性它自己不知道。这不是漏填的问题,是填了但把两个实体粘成了一个。
这类错误在校验器里很难暴露,因为每一个节点单看都合法。JSON-LD校验调试那篇处理的是语法层——尾逗号、引号、编码;@id撞车是语义层的问题,语法完全正确,含义已经变了。
顺带说,@id这个机制本来是为合并服务的。@graph与知识图谱那篇讲的正是它的正面用法:把一个组织写一次,其余节点用同一个@id指过去,几份标记就连成了一张图。ritual这个案例是同一个机制的反面——它没打算合并,但写法上等于下了合并的指令。
mainEntityOfPage和WebPage节点,有几个站写了?
除了给商品节点写地址,标准里还有两条更直接的路可以说明这一页在讲什么。
第一条是mainEntityOfPage:在商品节点上写明它是哪个页面的主要内容。本批2700个Product节点里,写了的有24个,占0.9%。24个全部来自同一个站。
第二条是反过来写:先声明一个WebPage类型的节点代表这一页,再用mainEntity指向那件商品。本批392个页面里,写了WebPage一类节点的有40页,占10.2%,来自5个站,每个站8页——也就是说这5个站是整站都写,其余48个站一页都不写。
而在这40页里,带了mainEntity属性的页面数是0。它们都声明了这是一个网页,没有一个说清这个网页的主角是谁。
| 指认方式 | 覆盖情况 | 实际有效的 |
|---|---|---|
| canonical标签 | 96.8%的页面写了 | 94.9%指向自己 |
| Product节点带完整地址 | 56.9%的节点写了url | 50.9%的页面能确定主体 |
| WebPage节点 | 10.2%的页面写了 | 0页带mainEntity |
| mainEntityOfPage | 0.9%的节点写了 | 集中在1个站 |
这张表从上往下,是同一件事的四种表达方式,普及率相差近百倍。越靠下的写法越明确,用的人越少——不是因为它们难写,一行的事;是因为没有任何一个环节会因为缺了它而报错。
面包屑那次实测量出人看到的路径和机器读到的只有16%对得上,成因也在这儿:格式全对、内容各写各的,中间没有一道会拦人的关口。
结构化数据里的商品名和页面上的h1,对得上吗?
既然地址那条路一半的页面走不通,那还有一条软一点的线索:名字。如果结构化数据里的商品名和页面上的h1是同一个,机器至少能靠文字对上。
338个页面能做这个比对,303页对得上,33页对不上。把这33页拆开,成因分四类,每一类的性质完全不同:
| 成因 | 样例 | 严重程度 |
|---|---|---|
| HTML实体没解码 | 结构化数据里写着Farmer's Market Alphabet,页面上是Farmer's Market Alphabet | 字面不等,语义相同 |
| 取到的h1不是商品名 | 结构化数据写Custom Shampoo,页面第一个h1是Your cart | 页面结构问题 |
| h1里塞了别的信息 | 结构化数据写Body Wash,h1是Stone Body Wash 18oz|$8 | h1被当成了展示位 |
| 结构化数据里写的是变体名 | 页面在卖一款手表,节点名字是Gold Black / M | 主体标错了 |
第一类最常见也最无害,framebridge三个页面都是这个毛病——转义了两遍,把'当成字面量写进了JSON。它不影响理解,但会让任何字符串比对的自动检查报出假问题。
第二类值得展开。functionofbeauty的五个商品页,页面上第一个h1是Your cart——购物车抽屉的标题在DOM里排在商品标题前面。用户看不见它,因为抽屉是收起来的;程序按顺序取第一个h1,取到的就是它。首页的title、og:title和H1说的常常不是同一件事那篇量的是首页三处自我描述互不一致,这里是另一回事:h1本身没写错,是它在文档里的位置不对。
第四类最要命。ice-watch的四个页面,结构化数据里的商品名是Gold Black / M这种颜色加尺码的组合,页面h1才是真正的表名。这说明它的模板把变体对象当成了主商品来输出——不是名字对不上,是主体本身选错了。这一类跟品牌字段那次看到的脏值直通是同一种传导:模板拿到什么就往外写什么,中间没有一步在问这个值合不合理。
一个商品页应该有几个h1?
做上面那个比对的时候,顺手数了一遍h1的数量,结果自成一个话题:
| 页面上的h1数量 | 页数 |
|---|---|
| 0个 | 43 |
| 1个 | 255 |
| 2个 | 57 |
| 3个 | 36 |
| 4个 | 1 |
一个h1的占了65%,其余三分之一要么没有,要么不止一个。
多个h1这件事,规范上是允许的,搜索引擎也早就说过不会因此扣分。但它跟本文的主题直接相关:当一页有三个h1的时候,把h1当成商品名的那套自动逻辑就不成立了,无论是我的检查脚本,还是任何一个想从页面上认出商品名的程序。
没有h1的那43页更麻烦一些。rothys、tentree、decathlon、materialkitchen这几个站是整站没有——我复验过,等到22秒、滚动过页面,仍然是0个。这些站的商品名只存在于title标签和结构化数据里,页面正文那一层没有任何标题级别的信号。对一个按无障碍树来读页面的智能体来说,这一页是没有标题的。
canonical做对了,为什么JSON-LD这一层没有?
同一批页面,canonical的写入率96.8%,指向自己的94.9%;结构化数据里能确定主体的50.9%。两个数字差得这么远,值得想一想原因。
我的判断是三件事叠出来的。
第一,canonical有过一次全行业的疼
参数页、分页、跨域抄袭这些问题在十几年里反复咬人,每一次都很痛,于是canonical从一个可选项变成了默认动作。建站平台默认输出它,SEO插件默认输出它,审计工具把缺canonical列成红色错误。结构化数据里的主体指认从来没有过这样一次疼——写不写,短期内看不出任何差别。
顺带一提,这也解释了为什么各种结构化数据生成器的默认产出里通常没有@id:它们按富媒体类型的必填项生成,而这一项不在必填项里。
第二,检查工具不查这一项
富媒体测试和结构化数据校验器查的是必填字段有没有、格式对不对。@id缺失不报错,mainEntityOfPage缺失不报错,两个节点@id撞车也不报错。结构化数据审计工具那篇讲的字段缺漏检查,同样不包含这一层——因为它不属于任何一个富媒体类型的必填项。
第三,主体指认的收益是延迟的
它的价值不在今天的富媒体结果上,在跨来源对齐上。schema堆满了但实体之间的关系没人审那篇讲的是同一件事的上层:一个能被稳定引用的标识,才能让这件商品在不同数据源之间被认成同一件。当购物代理需要把你的商品页、你的商品数据文件和第三方的价格记录对上号时,这个标识就是那根线。
值得留意的是,AI会不会推荐根本不存在的商品那篇讨论的幻觉问题,根子有一部分也在这儿:当来源本身就说不清哪一件是哪一件,下游的拼接只能靠猜。
自己的商品页怎么补上这个身份?
这件事的好处是改起来很便宜——它不新增任何内容,只是把已经存在的东西标清楚。
第一步:先测一遍现状
在商品页的控制台里,把所有Product节点的url和@id取出来,跟location.href去掉查询参数之前的完整地址比一比。比对的时候一定要用完整地址,别学我第一版把参数扔掉。结果只有三种:正好一个对上、多个对上、一个都没有。
第二步:给主商品节点写上三样东西
主商品节点上补齐@id、url和mainEntityOfPage,三个值都指向这一页的规范地址,跟canonical保持一致。@id用绝对网址,不要用相对路径,更不要用一个井号。商品条码栏那次实测比的是handle、规范地址和og:url这三处外部地址对不对得上,这一步是它的内部版本——把结构化数据里那份也接进同一个地址。
第三步:给变体节点一个不同的标识
变体节点的@id要跟主商品区分开,通常是主地址加变体参数。这一步的目的不是让变体被单独收录,是防止出现ritual那种两个节点共用一个标识的情况。
第四步:把这一项写进上线检查
因为没有任何现成工具会替你查,这一项必须自己加进检查清单。判据一句话:这一页的结构化数据里,有且只有一个节点的地址等于这一页的规范地址。多了是歧义,少了是没认领。
顺带把url这一栏也理一遍
本批取样的Product节点里,url写成绝对地址的896个,写成相对路径的60个,另有479个带着查询参数。相对路径那60个跟@id同理,解析方得自己拼基地址;带参数的那479个多数是变体节点,它们本来就该带参数,问题不在这儿。
真正要检查的是主商品节点那一个:它的url应该是不带参数的规范地址,跟canonical一模一样。如果主节点的url上也挂着一个变体参数,那这一页交出去的主体就是某个具体尺码,不是这件商品——ice-watch那四页就是这么来的。
什么时候不必做
如果你的商品页只有一个Product节点、没有变体展开、没有推荐位带出来的其它商品,那机器不会认错,这件事可以放到很后面。本批有近六成页面属于这种情况。页面类型声明那次实测里类目页的声明率只有三分之一,对多数站来说,那一项的优先级比这一项高。
真正该马上做的是三种页面:变体展开超过十个的、带商品推荐位的、以及模板里挂了多个第三方标记来源的。这三种页面上,节点多到机器必须做选择,而你没有给它任何依据。
常见问题解答
不写@id,商品富媒体结果会受影响吗?
短期不会。@id不在任何富媒体类型的必填字段里,缺了不影响价格和评分的展示。它影响的是跨来源识别——当同一件商品出现在你的页面、你的商品数据文件和第三方记录里,一个稳定的标识决定了这几份数据能不能被认成同一件。
@id应该填什么值?
填这一页的规范地址,用完整绝对网址,跟canonical保持一致。如果同一页里有多个需要区分的实体,可以在地址后面加锚点作为后缀。要避免的是相对路径、纯锚点,以及一页里多个节点共用一个值。
mainEntityOfPage和WebPage的mainEntity,选哪个写?
写一个就够。前者是在商品节点上指向页面,后者是在页面节点上指向商品,方向相反、效果一样。多数商品页模板只输出一份商品标记,那么在商品节点上加mainEntityOfPage是改动最小的做法。
变体节点也要写@id吗?
要写,而且必须跟主商品的值不同。变体节点最常见的做法是用主地址加变体参数。真正要避免的是所有变体共用一个@id,那等于告诉解析方它们是同一个东西。
页面上有多个h1,需要改吗?
从排名角度看不需要,搜索引擎明确说过多个h1不扣分。但如果你的商品页第一个h1是购物车或者别的组件标题,建议调整DOM顺序,因为很多自动化工具默认取第一个h1当页面主题,这个错会一路传下去。
怎么快速判断自己站上有没有这个问题?
看两个数就行:这一页有几个Product节点,其中有几个的完整地址等于本页地址。第一个数大于1、第二个数不等于1,就属于本文说的情况。这个检查在控制台里几行就能跑完,不需要任何工具。
这件事和canonical有冲突吗?
没有,两者应该保持一致。canonical告诉搜索引擎这一页的规范地址,结构化数据里的@id和url告诉它这份标记描述的实体在哪儿。两处写同一个地址,信号才是一致的;写成两个不同的值,反而会制造新的歧义。
权威参考资料
本文标题:《商品页的结构化数据里,一半找不到哪个节点是这一页》
本文链接:https://zhangwenbao.com/jsonld-main-entity-identification-audit.html
版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0
← 上一篇
表单提交交给谁?收邮箱那张表一半没写去向下一篇 →
没有了