Technology Approach
JSON-LD 作为首选格式
结构化标记可以通过多种方式以及多种不同的标准来实现。鉴于谷歌严格遵守其规范,我们对 schema.org 定义的标准特别感兴趣。
Schema.org 标记可以通过多种方式添加到网页中。在所有可用的方法中,我们认为在页面的 HTML 源代码中包含 JSON-LD 是(当前)最佳方法。
JSON-LD 提供了我们实现一致且可扩展基础所需的灵活性、可扩展性和标准化。虽然它缺少“内联”(结构化标记直接作为表示这些实体的 HTML 代码的一部分实现)的一些潜在好处,但其优势、灵活性和可扩展性远远超过了这些限制。
ID、关系和嵌套
JSON-LD 允许属性通过 ID 引用其他片段。例如,产品页面 无需包含(或重复)销售该产品的 组织 的所有标记,只需引用代表该 组织 的片段 ID 即可。 理论上,这使我们能够避免重复共享属性,并减少表示页面内容所需的代码/处理/开销。
遗憾的是,这项技术的 跨页面 支持水平有限——Google 的文档表述模糊,且 ID 与 URI 之间的关系存在歧义(据传闻,他们“无法从其他页面提取结构化数据”[原文如此])。鉴于这一限制,我们要求每个页面输出所有相关片段,并通过 hasPart、isPartOf、mainEntityOfPage 及类似的查找机制进行交叉引用。
例如,以下(简化后的)JSON 片段定义了一个 Organization,并将该 Organization 作为 WebSite 的 Publisher 进行引用:
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": "https://www.example.com/#/schema/Organization/1",
"url": "https://www.example.com/",
"name": "Example organization name"
},
{
"@type": "WebSite",
"@id": "https://www.example.com/#/schema/WebSite/1",
"url": "https://www.example.com/",
"name": "Example website",
"publisher": {
"@id": "https://www.example.com/#/schema/Organization/1"
}
}
]
}
在此示例中,我们知道网站的 publisher 与 Organization 相同。由于图中已存在代表该 Organization 的顶级片段,我们只需在 WebSite 片段中通过其 @id 引用它。这提供了极大的灵活性,并避免了重复。
基于此能力,我们倾向于尽可能避免属性的深度嵌套,并更愿意拆分出独立的片段,如下文示例所示。这使代码保持可读性、模块化和可扩展性。
构建 ID 参数
ID 参数通常应采用以下结构,下文列出的情况除外:
{{website}}/#/schema/{{type}}/{{ID}}
例如:
https://www.example.com/#/schema/organization/abc123https://www.example.com/#/schema/person/abc123https://www.example.com/#/schema/location/abc123https://www.example.com/#/schema/address/abc123https://www.example.com/#/schema/website/abc123https://www.example.com/#/schema/product/abc123https://www.example.com/#/schema/offer/abc123https://www.example.com/#/schema/article/abc123https://www.example.com/#/schema/breadcrumb/abc123https://www.example.com/#/schema/howto/abc123https://www.example.com/#/schema/itemlist/abc123
请查阅相关片段文档以了解正确的结构(如果已定义)。
异常情况
- images (
ImageObject) 的 ID 应为图像文件的完整绝对 URL(例如:https://www.example.com/images/cat.jpg?size=large)。
注意:
website是网站的协议+主机名(例如:https://www.example.com)。type应始终强制转换为小写,并移除所有空格/分隔符。
{{identifier}} 组合规则
{{identifier}} 参数应始终按以下层级顺序构建:
- 全局唯一且稳定的系统 ID(例如对象的数据库 ID)
- 根据上下文合成的全局唯一稳定 ID(例如
abc123-3表示与 ID 为abc123的product关联的第三个offer) - 实体所在
WebPage内的局部顺序整数(例如页面中第四个itemlist使用4)
Exceptions
These are also documented in their various pieces documentation, but bear repeating here.
- The ID of a
WebPageshould always be the unmodified canonical URL of the page (i.e. the permalink). - The
{{identifier}}fragment of theOrganizationwhich represents the site should always be1(e.g., https://www.example.com/#/schema/organization/1). - The
{{identifier}}fragment of theWebSitewhich represents "this site" should always be1(e.g., https://www.example.com/#/schema/website/1). - The
{{identifier}}fragment of apersonshould always be obfuscated.
混合类型与复合 ID
当节点为混合类型(即 @type 为值数组,例如 ['Organization', 'Person'])时,{{type}} 值应按字母顺序连接 @type 值,并用连字符分隔(例如 organization-person)。
主要实体
我们的模型假设每个 URL 都应代表一个主要实体——无论是组织、产品、博客文章(或博客文章集合)、个人还是其他事物。
我们始终致力于让该“主要实体”成为每个页面网络图谱的中心。这种心智模型与我们希望搜索引擎理解我们网络的方式高度契合;它使我们能够以如下方式阐述内容:“此URL代表一个食谱,它是文章的一部分,由个人撰写,位于网页上,而该网页属于网站的一部分,该网站由组织运营”。
本文档中的所有代码示例均体现了这一方法;我们通过使用hasPart、isPartOf、mainEntityOfPage及类似连接来构建实体间的定向关系。
代码碎片化与放置位置
有时无法将代码放置在 <head> 中,例如受架构限制,在构建 <head> 时可能无法访问必要的上下文。考虑到 <body> 内容有时可能包含需要反映在结构化数据中的内容或逻辑,我们也允许在页面末尾、闭合标签 </body> 之前输出代码。
由于我们使用 @id 属性来连接片段,从技术上讲,可以通过多个 <script> 标签和图结构,将代码拆分并分布在整个页面中,并按照本文档概述的方法通过 ID 简单交叉引用实体。还可以通过在页面其他位置创建具有相同 ID 的新引用来扩展现有片段。
我们通常建议系统开发者尽量避免这种碎片化(因为它会给本已复杂的系统引入脆弱性和混淆),但也认识到有时这是必要的。
事实上,当无法在 <head> 中计算和输出所有内容时,我们自己的某些解决方案也采用这种方法。例如,我们的 Yoast WooCommerce SEO 插件 依赖于解析初始化期间不可用的产品信息,因此在页面页脚输出第二个 <script> 块,其中包含对页面图的补充(特别是产品和评论信息)。这个附加图会无缝拼接,形成一个连贯的整体。
混合类型
有时,一个对象可能兼具两种不同事物的特性。例如,一本书既可以是 book 又可以是 product,并同时拥有两者的属性。它可能既有 author 属性,也有 price 属性。
添加多种类型能够提供更大的灵活性,并更精确地描述对象。
然而,在我们的方法中,我们谨慎使用混合类型,因为它们可能会模糊特定页面的“焦点”。如果页面的主要实体(或 URL)是一个复杂的复合类型,我们就有可能偏离“一个 URL,一个事物”的模型。