主题
第 15 章 结构化数据与富结果:把页面事实准确地交给机器
15.1 从一段价格文字到一个明确的数据对象
页面写着“青禾台灯,二百九十九元,有货”,人通常能够理解哪个是商品、哪个是价格。程序却需要区分这是一件商品、另一篇文章引用的价格,还是已经过期的活动文案。结构化数据 structured data 用明确的类型和字段表达这些事实,帮助机器理解页面描述的对象及其关系。
本章目标是让读者能选择合适标注,写出可维护的 JSON-LD,区分语法正确、功能资格与实际展示,并为真实数据缺失设计诚实的处理方式。它不教人给任何页面套上最多标签,而是从“这张页面实际向用户提供什么”开始。
Google 支持 JSON-LD、Microdata 和 RDFa 等格式,并通常推荐容易维护的 JSON-LD。Schema.org 定义了许多类型与属性,而 Google 的具体搜索功能只使用其中一部分;Google 功能的资格应查 Search Central 对应文档。[D13] 因此,词汇表允许一个字段,并不意味着某个搜索结果界面会使用它。
富结果 rich result 指相较普通文字结果更丰富的呈现方式,例如符合条件的产品信息或面包屑。正确标注只是资格条件的一部分。Google 明确不保证通过测试的标注一定展示,并要求标注真实、与页面主要内容相关、用户可见且符合具体政策。[D14] 不能把测试界面的绿色勾号写进商业合同,承诺客户一定出现星级。
15.2 先把对象、字段和关系讲清楚
Schema.org 的基础结构是类型 type 与属性 property:商品属于 Product,文章属于 Article,机构属于 Organization;对象再带有名称、网址等属性。[D18] 对程序员来说,可以把它理解为面向外部系统的公共数据模型,但不要把它等同于网站内部数据库。数据库里可能有客服备注、成本价和用户邮箱,公开标注只应包含适合公开、与页面相关的事实。
JSON-LD 是用 JSON 表达链接数据的方式。@context 说明使用的词汇上下文,@type 说明对象类型,@id 可以给对象一个稳定标识;正式语义由 W3C 的 JSON-LD 规范描述。[D43] 初学者不需要先读完完整标准,但要理解这些字段不只是“让测试通过的固定字符串”。
@id 与 url 可以有关联,却不必承担同一任务。一个页面 URL 可能是 /about,组织对象的稳定标识可以是 /#organization,作者对象可以是 /authors/lin#person。这样文章引用作者时,不必每次创造一个看起来相同却无法关联的实体。稳定标识是一种数据组织选择,不保证搜索引擎因此给出知识面板。
也不要随意使用 sameAs。它通常用于指向同一个对象在其他地方的明确标识,而不是“我喜欢的同行”“报道过我们的新闻”“所有相关关键词页面”。如果一条链接只是介绍、引用或合作关系,应依据真实关系选择合适表达。错误等同会让数据混淆,字段多并不能抵消语义错。
同一页可以描述多个对象。文章、作者、发布机构与面包屑并不互相排斥。关键在于读者能够在页面上理解这些对象,程序之间的关系也连得起来。相反,把组织、产品、食谱、活动、教程都塞进每个页面,只会增加无关信息和维护成本。
15.3 一份文章与面包屑的原创示例
以下示例用于虚构的“青禾工具文档”网站。作者、日期和地址均为教学数据,不代表真实人物、真实文章或已验证结果。示例未提交到验证工具,也未部署。正式使用前必须替换数据,并以对应功能的当前文档检查完整性。
代码块 · 51 行 · 点击收起 / 展开
html
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": "https://example.com/#organization",
"name": "青禾工具文档",
"url": "https://example.com/"
},
{
"@type": "Person",
"@id": "https://example.com/authors/lin#person",
"name": "林示例",
"url": "https://example.com/authors/lin"
},
{
"@type": "BlogPosting",
"@id": "https://example.com/guides/json-errors#article",
"headline": "理解 JSON 解析中的常见错误",
"datePublished": "2026-09-02T09:00:00+08:00",
"dateModified": "2026-10-01T10:30:00+08:00",
"author": {
"@id": "https://example.com/authors/lin#person"
},
"publisher": {
"@id": "https://example.com/#organization"
},
"mainEntityOfPage": "https://example.com/guides/json-errors",
"image": "https://example.com/images/json-errors-cover.jpg"
},
{
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "帮助文档",
"item": "https://example.com/guides"
},
{
"@type": "ListItem",
"position": 2,
"name": "JSON 错误排查",
"item": "https://example.com/guides/json-errors"
}
]
}
]
}
</script>阅读这段代码时,先忽略花括号,核对页面应该出现的事实:文章标题、作者链接、发布时间、修改时间、相关图片、可理解的栏目路径。文章字段应来自内容管理系统里的同一条记录,不能正文标题已经更新而结构化数据仍来自另一份手写配置。
Article 系列标注可以帮助解释文章标题、作者、图片和日期等信息,当前 Google 文档没有把 Article 标注作为进入新闻头条功能的必要条件。[D37] 因此,个人技术博客不应为了追求“新闻资格”随意把全部文章改成 NewsArticle。类型应忠于内容性质,文章的可信度还要由实际作者、证据与编辑过程支撑。
面包屑 BreadcrumbList 表达用户理解的导航路径。Google 建议它代表典型用户路径,不必机械复制 URL 的目录层级。[D19] 假设一篇文章的地址因历史原因是 /posts/2026/9087,它仍可在页面显示“帮助文档 → JSON 错误排查”。但不要为了关键词添加网站中根本不存在的栏目;用户点击后应能到达真实、相关的集合页。
一个对象可能被多个内容页面引用,最好统一生成。若模板插件把组织名称写成旧品牌,手工脚本又写成新品牌,同页两个对象都自称主机构,验证器可能无法替你判断哪个符合现实。应先指定数据归属,再删去重复或矛盾的输出,而不是试图用更多 @id 掩盖业务数据混乱。
15.4 不同业务该从哪些标注开始
对于个人站,先把作者、文章和站点本身的信息做准。真正由个人写作,就应能从署名进入作者介绍,了解专业背景与相关作品;不要把职业头衔、宣传语和关键词全塞进姓名字段。个人简介里的事实应可以解释,缺少可验证资历时不能借 schema 增添不存在的认证。
对于工具 SaaS,可以评估 SoftwareApplication 或适用子类型。不过 Google 软件应用富结果有明确所需属性,包含名称、报价价格,以及符合规则的评分或评论。[D38] 一个新工具没有真实评价,就不应复制示例中的五星和评论数量凑齐资格。可以诚实保留适当的语义标注,也可以暂不追求该富结果,先把产品与文档建设好。
免费工具与免费试用也要分清。完全免费使用与“先试七天,之后订阅收费”不是同一个报价。若页面包含多个套餐,应明确各套餐事实与适用范围,避免把最低价格拿来描述实际上不同的服务。搜索用户看到错误价格后进入网站,即使带来点击,也可能带来失望和投诉。
电商应先分清商品摘要与商家商品展示。Google 产品文档区分不能直接购买的介绍或评测场景,以及用户可以向商家购买的商品页;它们使用的信息与资格存在差异,但不是互斥分类:满足商家商品展示要求的页面,通常也可能符合商品摘要条件。[D16] 联盟评测博客不能仅因为推荐商品,就照搬自己作为卖家的库存和退货信息。
有颜色、尺码等变体时,应区分产品组与具体可购买版本。Google 提供 ProductGroup 与 Product 相关的变体说明。[D40] 实施前先决定哪个 URL 对应组、哪个 URL 代表具体变体,页面选择器改变时,显示价格、库存、图片和机器标注是否仍一致。变体信息是商品模型问题,不能只在前端添加一个标签了事。
例如一个页面切换全部颜色、尺码,可让 /coat?color=green&size=s 直接选中对应变体,同时为整个产品组保留一个规范地址,如 /coat。若蓝色与绿色分别有完整、同等重要的独立页面,就不应为整个产品组强行指定唯一规范页;每个页面应提供自足的组与变体标注,不能依赖另一页的 JSON-LD 才看得懂。[D40] 验收时复制具体变体 URL,在新会话直接打开,核对图片、价格、库存和加入购物车的 SKU;只从默认页点击选择器成功还不够。
本地服务站可考虑与真实门店类型匹配的 LocalBusiness 标注,例如地址、电话和营业时间。Google 的本地商家文档描述这些属性的用途与要求。[D39] 若企业只提供上门服务而没有公众可到访的门店,不要为了补地址制造一个虚构门面;若有多家门店,则应避免每个门店页都标成总部同一地址。
评论最容易被误用。对 LocalBusiness 或 Organization,当被评价对象控制其网站上关于自己的评价时,该页面不符合星级评价展示条件;嵌入第三方评价组件也不能绕过这条限制。[D41] 这项针对商家与机构自身的限制,不等于电商不能标注真实商品评价。也不能把全站服务评分复制成每个商品的评分。
评分必须有真实依据,但并非每条星级都必须附文字评论;ratingCount 表示评分数量,reviewCount 表示评论数量,不能凭空互换。当前政策还禁止虚假评价和未清楚披露激励的评价,例如以折扣换来的评价却不说明安排。[D41] 核对可见评价、统计来源、所评对象和激励披露,比只让评分字段通过语法检查更重要。
15.5 功能会变化,模板不能停在旧教程里
截至 2026-10-13 的核验结果,Google 官方更新记录写明 FAQ 富结果从 2026-05-07 起不再展示,并在 2026-06-15 删除了相关文档。[D17] 因此,旧教程中“普通站不能展示,政府和健康权威网站仍可展示”的说法已经不适合作为当前实施依据。
这不代表网站不应该提供常见问题。只要能回答用户真实疑问,FAQ 内容仍可帮助理解产品与减少重复咨询。需要调整的是项目目标:不要为已经停止的 Google 功能承诺额外展现,也不要把 Schema.org 中仍存在某个类型等同于 Google 仍提供对应富结果。
实际维护可以建立一份功能清单,记录所用类型、目标搜索功能、当前官方文档、负责人和最近核对日期。产品发布、搜索平台公告或模板大改时重新检查。Google 的支持功能目录适合用作入口,但每种功能仍需打开自己的说明核对,不应把一张几年前的截图当成长期规范。[D15]
面对功能下线,先调查已有标注是否还有其他明确用途,再决定保留、简化或移除。不要为了清理过期代码误删可见内容,也不要为一个已没有目标用途的复杂插件持续承受维护和性能成本。决定应写清依赖关系,而不是把“多写总没坏处”当成默认答案。
15.6 验证分四层,先确定自己在验证什么
第一层是语法。JSON 是否能解析,是否有尾随逗号、未转义引号、被模板截断的字符串?这一层可以在开发阶段自动测试。通过语法检查,只说明文本是合法 JSON,不说明字段类型正确,更不说明内容真实。
第二层是词汇与数据模型。使用 Schema Markup Validator 等工具检查类型和属性之间是否合理。[D42] 比如把日期写成价格,或把组织放入本应描述具体商品的位置,可能需要模型层检查。但某个通用验证器允许一个字段,仍不表示 Google 某项功能支持它。
第三层是搜索功能。使用 Rich Results Test 检查 Google 支持功能及对应错误。代码测试有助排除字段问题,URL 测试还能观察线上资源是否可被获取。实际结果应记录时间、测试地址和部署版本;不要把本地粘贴代码通过,写成整个线上站点通过。
第四层是事实与政策。人工对照可见内容、后台记录和业务规则,确认价格、库存、评价、日期和关系是否真实。自动工具通常不能判断一条五星评论是否伪造,也不能判断页面把两种套餐混成一个报价是否误导。所有自动检查通过后,这层仍不能省略。
通过四层检查后,才开始观察搜索引擎是否抓取了新版本、是否在相关报告中识别有效对象,以及在实际展现中是否采用相应形式。这里的每一步都有不同时间与覆盖范围。如果工具没有报告某类型,先确认它是否支持该类型,不要立刻推断语法有错。
15.7 用同一份数据生成正文与标注
维护结构化数据最有效的办法,是减少独立维护的事实副本。假设商品价格由业务系统返回,正文使用业务系统价格,而 JSON-LD 每天由另一个脚本生成,那么促销开始和结束时就可能不一致。更可靠的设计是正文与标注共同读取一份发布模型,并对缓存失效设置一致规则。
发布模型不同于原始数据库记录。它已经决定哪些字段公开、如何格式化、选择哪个变体、采用哪个正式 URL。这个中间层既服务页面,也服务结构化输出,能减少各模板对同一业务字段的不同解释。即便网站只有几十页,这种分工也能避免手写 JSON 越积越多。
生成 JSON 时应使用序列化器,不要拼接字符串。文章标题可能含引号,商品描述可能含换行,用户评论还可能含看似 HTML 的文本。字符串拼接一旦处理不当,会同时造成语法错误与安全风险。嵌入 HTML 的 JSON 还要考虑 script 结束序列等上下文转义;不能把“合法 JSON”当成“在任何 HTML 上下文都安全”。
下面是原创的基础校验示范,已用 20 个本地样例检查,只接受单个文章对象、一个内嵌作者和可选的单个图片 URL。它不代表 Google 功能完整验证,也不替代 HTML 上下文安全处理。
代码块 · 48 行 · 点击收起 / 展开
python
import json
from urllib.parse import urlparse
def is_https_url(value):
if not isinstance(value, str) or not value.strip():
return False
if any(ch.isspace() for ch in value):
return False
try:
parsed = urlparse(value)
return (parsed.scheme == "https" and bool(parsed.hostname)
and parsed.username is None and parsed.password is None)
except ValueError:
return False
def validate_public_article(raw_json, expected_url):
try:
data = json.loads(raw_json)
except (json.JSONDecodeError, TypeError):
return ["输入不是可解析的 JSON 文本"]
if not isinstance(data, dict) or "@graph" in data:
return ["此简化模型只接受单个文章对象"]
problems = []
article_type = data.get("@type")
if (not isinstance(article_type, str)
or article_type not in {"Article", "BlogPosting", "NewsArticle"}):
problems.append("文章类型不符合本项目约定")
headline = data.get("headline")
if not isinstance(headline, str) or not headline.strip():
problems.append("缺少非空字符串标题")
if not is_https_url(expected_url):
problems.append("检查程序的预期地址配置有误")
if data.get("mainEntityOfPage") != expected_url:
problems.append("文章地址与发布地址不一致")
if not is_https_url(data.get("mainEntityOfPage")):
problems.append("文章地址须为绝对 HTTPS 地址")
author = data.get("author")
if not isinstance(author, dict):
problems.append("此简化模型要求一个内嵌作者对象")
elif not isinstance(author.get("name"), str) or not author["name"].strip():
problems.append("此简化模型要求非空作者姓名")
if "image" in data and not is_https_url(data["image"]):
problems.append("此简化模型的图片须为单个绝对 HTTPS 地址")
return problems这段函数故意针对简化的文章对象,不能直接用于前面的 @graph 示例;引用式作者、多作者、图片数组和其他合法 JSON-LD 写法,需要扩展模型后再检查。HTTPS 检查只验证本项目约定的地址形式,不证明域名可信、资源可访问或内容真实;urlparse 本身也不是完整 URL 验证器。[D51] 测试应针对实际发布模型编写,而不能把一个简单脚本宣称为通用 schema 验证器。
更完整的测试集应包括中文引号、空字段、缺失图片、多个作者、修改但未重新发布、不同币种、售罄和已取消活动。测试缺失值时,应验证程序选择省略不适用字段或阻止发布,而不是自动填上“未知作者”“五星”或今天日期。这些默认值看起来整齐,却可能创造错误事实。
15.8 三个常见故障怎样逐步定位
第一个案例是价格不一致。假设用户页面显示促销价,富结果测试却读取到旧价格。先保存原始 HTML 和渲染后 HTML,搜索所有 JSON-LD 块。若两个插件分别输出不同 Product,就先定位每个块的生成者;若只有一个块且值陈旧,则查数据源与缓存。修复后应检查促销开始、促销结束和无促销三种状态,不只验证一个当前商品。
第二个案例是对象数量突然减少。团队可能以为 Google 改了算法,但新模板只是把 JSON-LD 移到一个仅登录用户可加载的接口。此时先比较发布前后相同 URL 的响应,再用未登录会话和搜索工具观察。若正文仍可读取而标注缺失,范围应限定为结构化输出,不必立即重写整篇文章。修复后记录新版本何时被重新抓取,搜索报告数量不会与代码发布瞬间同步。
第三个案例是测试通过但没有星级。先确认目标类型与功能仍受支持,再核实评分来源、可见性、对象归属和政策;再查看有无相关人工处置或索引问题。若这些都正确,仍可能只是搜索系统未选择该形式。不能继续造评论,也不能在每一页增加重复评分来“加强信号”。此时报告应写“符合已检查的技术条件,实际展示未获保证”,并转向可以控制的产品与内容工作。
还有一种更隐蔽的错误:图片 URL 可以打开,却需要短期签名,几小时后就失效。人工验收当天看起来正常,后续抓取却无法稳定取得图片。结构化资源应有可持续访问策略,签名链接和权限机制需要与公开用途协调。私人文件不应为 SEO 改成公开,应改用明确获准发布的图片。
15.9 衡量结果时不要把相关性写成因果
结构化数据上线后,适合先看覆盖和正确性:预期多少页面有标注,实际多少页面输出,多少对象语法无效,多少关键字段与可见内容不一致。覆盖率应以“适用页面”作分母,而不是全部 URL。登录页没有商品标注不是缺陷,空白标签页多加一个 Product 也不是进步。
第二步再看搜索展现。可以按页面模板、查询类别、设备与时间观察展现、点击率和点击,但要记下同期标题修改、促销、排名变化与季节因素。假设上线后一周点击率上升,不能直接归因为 schema,因为同一周可能减少了低排名查询展现。点击率的分母变化本身就会影响数字。
若流量规模允许,可让相似页面分批上线,尽量保持内容和其他设置稳定,并预先定义观察窗口与成功标准。对于流量很少的个人站,统计上未必能分辨小幅提升,此时更适合以数据正确、维护稳定、功能适配作为验收,不必虚构显著增长。
一个实用投入顺序是:先修与真实业务相关的严重错误,例如价格和库存;再补目标功能真正需要且能维护的字段;最后评估非必要扩展。不要为了消除所有“建议”警告收集根本不存在的数据。可选字段缺失与误导字段存在,风险并不相同。
15.10 把标注维护交给明确的人
结构化数据通常跨越内容、开发、商品和运营,因此每类字段都应有责任人。作者负责署名事实,编辑负责发布与修改日期,商品系统负责价格库存,门店运营负责营业时间,开发负责序列化与缓存。只把全部错误发给 SEO 同事,而不给修改数据源的权限,问题会反复出现。
应同时制定内容生命周期。商品停售、门店搬迁、活动取消、作者改名、文章合并,都可能改变标注。发布系统最好通过事件触发同步更新,而不是等搜索报告出现警告再修。对于需要保留历史的内容,要明确哪些字段描述当时事实,哪些字段表达当前状态,避免把已经发生的活动改成未来活动吸引展现。
在交付文档中留下示例地址、数据来源、目标功能、验证日期、已知局限和回归测试入口。下一位维护者即使不知道最初选择,也能判断字段为什么存在。结构化数据的质量取决于长期准确性,只有上线当天通过一次测试是不够的。
练习与参考解答
练习一:新 SaaS 没有用户评论,软件应用测试提示缺少评分或评论。能否用创始人自评补齐?参考解答:不能为了资格创造误导性评价。应核对当前功能要求,接受暂时不满足该富结果条件,同时准确描述实际软件事实。
练习二:Schema.org 验证通过,Google 测试却没有识别目标富结果。哪里可能有问题?参考解答:两种测试范围不同;该类型可能不受当前 Google 功能支持,也可能缺少功能所需属性。需查对应文档,而不是认定其中一个工具错误。
练习三:十家门店都从总部模板生成相同营业时间,暂时没有报错。是否可以上线?参考解答:先逐店确认事实。自动验证不能证明真实营业时间,错误信息会让用户白跑,且可能跨多个渠道扩散。
执行清单
- 从可见页面与真实对象出发选择类型,明确目标功能。
- 核对当前官方文档,不依据旧截图承诺富结果。
- 让正文与标注共享受控数据源,并测试缺失与状态变化。
- 分开完成语法、模型、搜索功能与事实政策四层验证。
- 记录覆盖、错误与实际展现,避免把上线前后变化直接当因果。
本章参考来源
[D13] Introduction to structured data markup | https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data | 访问 2026-10-13 | 格式、词汇与搜索功能的区别。 [D14] General structured data guidelines | https://developers.google.com/search/docs/appearance/structured-data/sd-policies | 访问 2026-10-13 | 内容真实性、可见性与展示边界。 [D15] Structured data markup that Google Search supports | https://developers.google.com/search/docs/appearance/structured-data/search-gallery | 访问 2026-10-13 | 当前功能目录。 [D16] Introduction to Product structured data | https://developers.google.com/search/docs/appearance/structured-data/product | 访问 2026-10-13 | 商品摘要与商家商品展示的区分。 [D17] Latest Google Search documentation updates | https://developers.google.com/search/updates | 访问 2026-10-13 | FAQ 富结果于 2026-05-07 停止展示、文档于 2026-06-15 移除。 [D18] Getting started with Schema.org | https://schema.org/docs/gs.html | 访问 2026-10-13 | 类型与属性的基础模型。 [D19] Breadcrumb structured data | https://developers.google.com/search/docs/appearance/structured-data/breadcrumb | 访问 2026-10-13 | 面包屑导航语义。 [D37] Article structured data | https://developers.google.com/search/docs/appearance/structured-data/article | 访问 2026-10-13 | 文章、作者与日期字段。 [D38] Software app structured data | https://developers.google.com/search/docs/appearance/structured-data/software-app | 访问 2026-10-13 | 应用富结果字段与评价要求。 [D39] Local business structured data | https://developers.google.com/search/docs/appearance/structured-data/local-business | 访问 2026-10-13 | 本地商家信息。 [D40] Product variant structured data | https://developers.google.com/search/docs/appearance/structured-data/product-variants | 访问 2026-10-13 | 产品组与变体模型;单页和多页实现的canonical与自足标注区别。 [D41] Review snippet structured data | https://developers.google.com/search/docs/appearance/structured-data/review-snippet | 访问 2026-10-13 | 评论归属、自利评论限制、评分数量与评论数量、虚假及未披露激励评价政策。 [D42] Schema Markup Validator | https://validator.schema.org/ | 访问 2026-10-13 | 通用标注验证入口。 [D43] JSON-LD 1.1 | https://www.w3.org/TR/json-ld11/ | 访问 2026-10-13 | JSON-LD 的标准语义。 [D51] urllib.parse — Parse URLs into components | https://docs.python.org/3/library/urllib.parse.html | 访问 2026-10-13 | URL解析不等于完整有效性验证。