学完这篇,你能给任何一个页面手写正确的 Schema.org 结构化数据,用官方工具验证通过,并避开那些“标记了却不出富摘要”的常见坑。
第一步:先分清微格式和结构化数据
这两个词经常被混着叫,其实不是一回事:
- 微格式(Microformats):靠 HTML 的
class 命名约定表达语义,比如 h-card、h-entry、h-review。它没有独立语法,解析器认的是 class 名。
- 结构化数据(Structured Data):用一套正式词汇表描述页面实体,主流词汇表就是 Schema.org,载体有三种:JSON-LD、Microdata、RDFa。
现在实际该用的是 JSON-LD。它躺在 <script type="application/ld+json"> 里,和页面结构解耦,改模板不会破坏标记,Google 也明确推荐。微格式留作了解即可。
注意:不要再在同一个页面同时堆 Microdata 和 JSON-LD 描述同一实体,容易产生冲突数据,选一种就行。
第二步:按页面类型选 @type
别一上来就写代码,先回答“这个页面是什么”:
| 页面 | 主要类型 |
|---|
| 帖子详情 | BlogPosting / Article |
| 单个问答帖 | QAPage + Question + acceptedAnswer |
| 问答列表、帮助中心 | FAQPage |
| 带评分的文章 | 主体类型 + aggregateRating |
| 站点首页 | Organization(配 sameAs) |
| 频道/版块导航 | BreadcrumbList |
类型选错,字段写得再全也没用。
第三步:写出第一段 JSON-LD
在 <head> 里加(放 body 也合法,但 head 更清爽):
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "HTML 微格式与结构化数据实战",
"datePublished": "2025-03-01T10:00:00+08:00",
"dateModified": "2025-03-05T09:30:00+08:00",
"author": { "@type": "Person", "name": "阿乐", "url": "https://example.com/u/1" },
"publisher": {
"@type": "Organization",
"name": "示例社区",
"logo": { "@type": "ImageObject", "url": "https://example.com/logo.png" }
},
"image": ["https://example.com/cover.jpg"],
"mainEntityOfPage": { "@type": "WebPage", "@id": "https://example.com/post/1" }
}
</script>
四个硬性要求,缺一个就容易报错:
- 日期用 ISO 8601,带时区,别写
2025-3-1;
- 所有 URL 用绝对地址,
/post/1 不行;
- 图片必须可公开访问,登录墙后的图抓不到;
@context 必须是 https://schema.org。
第四步:问答页别把 QAPage 和 FAQPage 用反
这是最高频的错误。两者的区别是:QAPage 描述“一个问题页”,FAQPage 描述“一组问答列表”。
单帖问答(比如社区里的悬赏帖):
{
"@context": "https://schema.org",
"@type": "QAPage",
"mainEntity": {
"@type": "Question",
"name": "PHP 8.3 下连接 MySQL 报错怎么排查?",
"text": "完整问题描述……",
"answerCount": 1,
"acceptedAnswer": {
"@type": "Answer",
"text": "最佳答案正文……",
"dateCreated": "2025-03-02T14:20:00+08:00",
"author": { "@type": "Person", "name": "回答者" }
}
}
}
聚合页用 FAQPage,mainEntity 是数组:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "问题一",
"acceptedAnswer": { "@type": "Answer", "text": "答案一" }
}
]
}
注意:acceptedAnswer 只应出现在确实已采纳答案的页面。未解决的帖子就别输出这个字段,只留 suggestedAnswer 或不加答案,否则属于“标记与内容不符”。
第五步:评分星级怎么才能出富摘要
给主体对象加嵌套的 aggregateRating,字段值是字符串或数字都行,但要真实:
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.6",
"reviewCount": "38",
"bestRating": "5",
"worstRating": "1"
}
reviewCount 必须是页面上真实可见的评价数量。自己写个 5.0 / 1 条 属于操纵排名,被识别到会吃人工处罚。
顺带说一句,Clara BBS 这类系统里这些是自动输出的——帖子评分均分写进结构化数据,悬赏帖页出 QAPage + acceptedAnswer,/answers.html 问答聚合页出 FAQPage,站长在后台「系统设置 → GEO 优化」里能看到相关开关。手写模板的话,就照上面的结构套。
第六步:验证,别靠肉眼
三件工具按顺序过一遍:
- Schema Markup Validator(validator.schema.org)——查语法和词汇表合法性;
- 富媒体搜索结果测试(search.google.com/test/rich-results)——查能不能出富摘要;
- Search Console → 改进项目——上线后看真实抓取报错。
注意:JSON-LD 必须是严格合法 JSON。不能有注释、不能有尾逗号、字符串必须双引号。另外,如果你把用户提交的标题、正文直接拼进 JSON,一定要先做转义(PHP 用 json_encode 整体生成,别手动拼字符串),否则一个引号就能把整段标记打挂,严重时是 XSS 入口。
小结
- 微格式靠 class 约定,Schema.org 靠词汇表;实战统一用 JSON-LD。
- 先定
@type,再填字段,类型错了全盘皆输。
- 日期 ISO 8601、URL 绝对地址、图片可公开访问,这三条是硬门槛。
- QAPage 用于单问题页,FAQPage 用于问答列表,
acceptedAnswer 只在真采纳时出现。
- 标记内容必须与页面可见内容一致,虚构评分是红线。
- 上线前用 Schema Markup Validator + 富结果测试双验证,上线后盯 Search Console。