前几天我向一群WordPress开发者询问了关于Schema的情况,结果让我感到惊讶。尽管几乎所有开发者都听说过Schema,并且意识到它带来的潜在好处,但实际上很少有人在项目中使用它。
如果您不熟悉Schema,它们是HTML属性,可以帮助搜索引擎理解内容结构以及如何在搜索引擎结果中正确显示内容。我们都参与过SEO至关重要的项目,因此Schema可以成为优化和提升搜索性能的关键交付成果。
在本文中,我们将更深入地探讨Schema的概念,然后逐步介绍如何在WordPress环境中使用它的实际应用。
Schema概述
Schema是描述文档内容的HTML属性和值的词汇表。这个词汇表的概念源于谷歌、微软、雅虎和Yandex成员之间的合作,并且此后已成为由这些创始组织以及来自W3C的成员和社区个人维护的项目。事实上,您可以在其开放社区页面上查看Schema社区的活动并与该小组联系。
在讨论Schema时,您可能会看到结构化数据这个术语,这是因为它很好地描述了Schema的工作原理。它们以数据形式提供词汇和层次结构,为HTML标记添加结构和细节。反过来,这使得搜索引擎更容易抓取、读取、索引和解释HTML文档的内容。如果您在某个地方看到结构化数据,那么我们实际上也在谈论Schema。
Schema格式
Schema可以通过三种不同的格式提供:Microdata、JSON-LD和RDFa。本文不会深入探讨RDFa,因为Microdata和JSON-LD占据了绝大多数用例。事实上,当我们稍后在本篇文章中深入研究一个工作示例时,我们将把全部注意力转移到JSON-LD上。
让我们通过一个商业列表网站的示例来说明Microdata和JSON-LD之间的区别,在这个网站上,访问者可以浏览本地企业的相关信息。每个企业将是一个项目,它有额外的上下文,例如企业类型、企业名称、描述和营业时间。我们希望搜索引擎读取这些数据,以便能够在返回搜索结果时清晰地呈现这些信息。您知道,类似于这样

以下是如何使用Microdata以类似的方式显示营业时间
<div itemscope="" itemtype="http://schema.org/Pharmacy">
<h1 itemprop="name">Philippa's Pharmacy</h1>
<p itemprop="description">
A superb collection of fine pharmaceuticals.
</p>
<p>Open:
<span itemprop="openingHours" content="Mo,Tu,We,Th 09:00-12:00">
Monday-Thursday 9am-noon
</span>
</p>
</div>
JSON-LD也可以实现同样的效果
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Pharmacy",
"name": "Philippa's Pharmacy",
"description": "A superb collection of fine pharmaceuticals.",
"openingHours": "Mo,Tu,We,Th 09:00-12:00"
}
</script>
Schema如何影响SEO
我们之所以谈论Schema,是因为我们关心搜索引擎如何解释我们的内容,因此,我们想知道Schema对网站的实际搜索引擎排名和性能究竟有多大影响。
谷歌的John Mueller 在2015年参加了一次视频聊天,并非常清楚地指出了Schema在搜索引擎优化领域的重要性。Schema项目由搜索引擎行业的巨头创建并维护这一事实表明,如果我们想获得良好的排名和索引,那么我们将把Schema视为SEO策略的一部分。
虽然可能存在其他网站和帖子提供更多数据来支持Schema的重要性,但我们应该指出的重点是它对用户体验的影响。如果有人在谷歌中搜索“Tom Petty演唱会门票”并获得一系列结果,很容易假设结果中突出显示即将到来的日期的结果是最突出的,并且是最容易识别最有用的,即使它不是列表中的第一个结果。

同样,这只是推测,其他帖子或网站可能提供数据来支持Schema对搜索结果排名的影响,但是对搜索引擎读取和显示我们内容的方式施加一点影响对于我们前端开发人员来说是一个不错的功能,我们会尽力而为。
决定使用哪种格式
归根结底,这取决于您的喜好。也就是说,谷歌的Schema文档几乎全部围绕JSON-LD展开,因此,如果您希望在谷歌结果中获得更多潜在影响,那么这可能是您的起点。谷歌甚至提供了一个方便的网站管理员工具,它可以生成JSON-LD数据,如果您刚开始使用,这可能是门槛最低的方法。
了解哪些数据可以结构化
谷歌的结构化数据指南是关于该主题最详尽和全面的资源,并提供了关于哪些数据可以结构化以及如何结构化的示例。
底线是Schema希望将内容分类为“类型”,截至本文撰写之时,这些是谷歌目前识别的类型:
- 文章
- 书籍
- 课程
- 数据集
- 事件
- 事实核查
- 职位发布
- 本地企业
- 音乐
- 播客
- 产品
- 食谱
- 评论
- 电视和电影
- 视频
除了内容类型之外,谷歌还会查找充当搜索结果UI增强功能的结构化数据
- 面包屑导航
- 网站链接搜索框
- 公司联系信息
- 徽标
- 社交资料链接
- 轮播
您确实可以开始看到我们拥有哪些机会来帮助影响搜索结果的显示内容和显示方式。
在WordPress中管理Schema
好的,我们已经花了相当多的时间深入探讨Schema的概念以及它们如何有利于网站的搜索引擎优化,但我们究竟如何使用它呢?我发现解决这个问题的最佳方法是通过一个实际示例,所以我们接下来将这样做。
在本例中,我们使用WordPress作为我们的内容管理系统,并将使用流行的高级自定义字段(ACF)插件。最终,我们将能够使用有效的JSON-LD格式动态生成内容的Schema。
一些读者可能会想在这里阻止我,并询问为什么我们不使用流行的WordPress SEO插件(如Yoast和Schema)的内置Schema管理工具。实际上,有很多WordPress插件可以帮助向网站添加结构化数据,使用其中任何一个都是一个您可以考虑的合法选择。根据我的经验,这些插件无法提供我在需要访问和控制所有内容类型的项目中所需的详细程度,例如本地企业的营业时间和联系信息。
这就是ACF发挥作用的地方!我们不仅可以创建精确的字段来捕获我们想要生成和提供的数据,还可以将其作为WordPress日常内容管理的一部分动态执行。
我们以一个本地商家(剧透一下 Content-Type,对吧?!)网站为例。我们将在 WordPress 中创建一个自定义页面,其中包含自定义字段,允许我们管理商家的结构化数据。
以下是它的样子

我已经将本文中所有有效的示例都放在一个 GitHub 仓库中,您可以将其用作起点,或者在我们分解实现步骤时跟随操作。
步骤 1:创建自定义选项页面
可以在我们的 functions.php
文件中直接设置 WordPress 的自定义管理页面。
// Create a General Options admin page
// `options_page` is going to be the name of ACF group we use to set up the fields
// We can use that as a conditional statement to create the page against
if (function_exists('acf_add_options_page')) {
acf_add_options_page(array(
'page_title' => 'General Options',
'menu_title' => 'General Options',
'menu_slug' => 'general-options',
'capability' => 'edit_posts',
'redirect' => false
));
}
这段代码片段会在 WordPress 导航中添加一个名为“常规选项”的新链接,但只有在下一步中连接 ACF 后才会出现。当然,您可以将其命名为任何您喜欢的名称。关键是我们现在有了一种创建页面和访问它的方法。
步骤 2:创建自定义字段
如果“常规选项”页面中没有任何内容,那么它就没有用处。安装并激活 Advanced Custom Fields 后,我们需要前往那里并设置捕获和存储结构化数据所需的字段。
以下是自定义字段的组织方式
- 公司徽标
- 公司地址
- 营业时间
- 休息日
- 联系信息
- 社交媒体链接
- Schema 类型
这里有很多字段,您可以使用 GitHub 仓库中的 acf-export.json
文件将字段导入 ACF,而不是手动创建所有字段。请注意,一些字段使用重复器功能,该功能目前仅在付费的 ACF 扩展中受支持。
步骤 3:将自定义字段链接到常规选项
现在我们已在 ACF 中设置了自定义字段,我们的下一个任务是将它们映射到我们的自定义“常规选项”页面。实际上,此步骤在创建自定义字段时就会出现。ACF 为每个字段组提供设置,允许您指定是否应在特定页面上显示这些字段。
换句话说,对于我们创建的每个字段组,请务必返回并确认已选择“常规选项”页面,以便这些字段仅在 WordPress 中显示在那里。

现在我们的“常规选项”页面拥有了一组我们可以管理的实际选项!
请注意:示例文件中数据的组织方式是我习惯管理 Schema 的方式。您可能会发现以其他方式组织字段更容易,这完全没问题。而且,当然,如果您使用的是与本地商家示例不同的内容类型,那么您可能不需要我们这里使用的所有字段,或者需要使用其他字段。
步骤 4:输入数据
好的,如果没有数据,我们的结构化数据就只是……嗯,结构化?无论那是什么,让我们输入数据吧。
- 公司徽标:Google 指定理想尺寸为
151px
正方形。如果 Google 在搜索结果右侧显示公司信息,它将使用此图像。您可以通过搜索知名公司(例如Google 本身)来查看此功能。 - 建筑物照片:这可以为显示公司徽标的相同公司简介卡添加一些趣味性,但此字段也会影响地图中的搜索结果。Google 建议使用
200px
正方形图像。 - Schema 类型:选择 Schema 的内容类型。在本例中,我们正在处理本地商家,因此它是内容类型。
- 地址:这些是相当简单的文本字段,将在搜索结果和与公司徽标相同的简介卡中使用。
- 营业时间:可以在schema.org 网站上找到营业时间的规范。我们在示例中设置此内容的方式是使用重复器字段,其中包含四个子字段以指定一周中的日期、开始营业时间、结束营业时间以及一个切换按钮以区分营业时间范围和休息时间范围。根据 Schema 文档,这应该涵盖了所有方面。
- 特殊日期:这些是假期(例如圣诞节),商家在这些日期可能不会在正常营业时间营业。Schema 提供这种灵活性非常棒,因为它允许用户在这些日期搜索时查看这些例外情况。
- 联系方式:联系数据提供了许多设置。在本例中,我们将使用其中的三个,即类型(用作业务部门,例如销售或客户服务)、电话(要拨打的号码)和选项(支持
TollFree
和HearingImpairedSupported
选项)。
步骤 5:生成 JSON-LD
这是关键所在。如果到目前为止,我们已经创建了一个管理数据的地方,创建了这些数据的字段,并实际输入了数据,那么现在我们需要获取这些收集到的数据,并将其输出为搜索引擎可以使用的格式。同样,GitHub 仓库包含了我们正在处理的内容的最终结果,但让我们深入研究该代码,看看如何从 ACF 中获取数据并将其转换为 JSON-LD。
要读取所有值并创建 JSON-LD 标签,我们需要进入 functions.php
文件并编写一个代码片段,将我们的 JSON 数据注入到网站标题中。我们将注入内容类型、地址以及 WordPress 中已存在的一些有关网站的数据,例如网站名称和地址。
// Using `wp_head` to inject to the document <head>
add_action('wp_head', function() {
$schema = array(
// Tell search engines that this is structured data
'@context' => "http://schema.org",
// Tell search engines the content type it is looking at
'@type' => get_field('schema_type', 'options'),
// Provide search engines with the site name and address
'name' => get_bloginfo('name'),
'url' => get_home_url(),
// Provide the company address
'telephone' => '+49' . get_field('company_phone', 'options'), //needs country code
'address' => array(
'@type' => 'PostalAddress',
'streetAddress' => get_field('address_street', 'option'),
'postalCode' => get_field('address_postal', 'option'),
'addressLocality' => get_field('address_locality', 'option'),
'addressRegion' => get_field('address_region', 'option'),
'addressCountry' => get_field('address_country', 'option')
)
);
}
徽标并不是真正必需的信息,我们将检查它是否存在,如果存在则获取它并将其添加到混合中。
// If there is a company logo...
if (get_field('company_logo', 'option')) {
// ...then add it to the schema array
$schema['logo'] = get_field('company_logo', 'option');
}
使用 ACF 中的重复器字段需要额外考虑一些问题,因此我们将不得不编写一个循环来获取和添加社交媒体链接。
// Check for social media links
if (have_rows('social_media', 'option')) {
$schema['sameAs'] = array();
// For each instance...
while (have_rows('social_media', 'option')) : the_row();
// ...add it to the schema array
array_push($schema['sameAs'], get_sub_field('url'));
endwhile;
}
添加“营业时间”字段中的数据有点棘手,但这仅仅是因为我们在营业时间范围和休息时间范围之间添加了额外的区分。基本上,我们需要检查作为字段一部分设置的 $closed
变量,然后输出时间,以便它们落入正确的组。
// Let's check for Opening Hours rows
if (have_rows('opening_hours', 'option')) {
// Then set up the array
$schema['openingHoursSpecification'] = array();
// For each row...
while (have_rows('opening_hours', 'option')) : the_row();
// ...check if it's marked "Closed"...
$closed = get_sub_field('closed');
// ...then output the times
$openings = array(
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => get_sub_field('days'),
'opens' => $closed ? '00:00' : get_sub_field('from'),
'closes' => $closed ? '00:00' : get_sub_field('to')
);
// Finally, push this array to the schema array
array_push($schema['openingHoursSpecification'], $openings);
endwhile;
}
我们可以使用几乎相同的代码片段来输出我们的“特殊日期”数据。
// Let's check for Special Days rows
if (have_rows('special_days', 'option')) {
// For each row...
while (have_rows('special_days', 'option')) : the_row();
// ...check if it's marked "Closed"...
$closed = get_sub_field('closed');
// ...then output the times
$special_days = array(
'@type' => 'OpeningHoursSpecification',
'validFrom' => get_sub_field('date_from'),
'validThrough' => get_sub_field('date_to'),
'opens' => $closed ? '00:00' : get_sub_field('time_from'),
'closes' => $closed ? '00:00' : get_sub_field('time_to')
);
// Finally, push this array to the schema array
array_push($schema['openingHoursSpecification'], $special_days);
endwhile;
}
最后一部分是我们的“联系信息”数据。同样,我们正在使用一个循环来创建一个数组,然后将其注入到 Schema 数组中,该数组又会注入到文档 中。
请注意,电话号码需要国家代码,您可以将其替换为您自己的国家代码。
// Let's check for Contact Information rows
if (get_field('contact', 'options')) {
// Then create an array of the data, if it exists
$schema['contactPoint'] = array();
// For each row of contact information...
while (have_rows('contact', 'options')) : the_row();
// ...fetch the following fields
$contacts = array(
'@type' => 'ContactPoint',
'contactType' => get_sub_field('type'),
'telephone' => '+49' . get_sub_field('phone')
);
// Let's not forget the Option field
if (get_sub_field('option')) {
$contacts['contactOption'] = get_sub_field('option');
}
// Finally, push this array to the schema array
array_push($schema['contactPoint'], $contacts);
endwhile;
}
让我们惊叹于我们的作品!
现在我们可以将数据编码为 JSON,并在我们的 add_action
函数结束之前将其放入脚本标签中。
echo '<script type="application/ld+json">' . json_encode($schema) . '</script>';
读者 Mark Gombar 建议使用 json_encode($schema, JSON_UNESCAPED_SLASHES)
以避免 URL 中出现转义斜杠。
最终的脚本可能如下所示
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Store",
"name": "My Store",
"url": "https://my-domain.com",
"telephone": "+49 1234 567",
"address": {
"@type": "PostalAddress",
"streetAddress": "Musterstraße",
"postalCode": "13123",
"addressLocality": "Berlin",
"addressRegion": "Berlin",
"addressCountry": "Deutschland"
},
"sameAs": ["https://facebook.com/my-profile"],
"openingHoursSpecification": [{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Mo", "Tu", "We", "Th", "Fr"],
"opens": "07:00",
"closes": "20:00"
}, {
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Sa", "Su"],
"opens": "00:00",
"closes": "00:00"
}, {
"@type": "OpeningHoursSpecification",
"validFrom": "2017-08-12",
"validThrough": "2017-08-12",
"opens": "10:00",
"closes": "12:00"
}],
"contactPoint": [{
"@type": "ContactPoint",
"contactType": "customer support",
"telephone": "+491527381923",
"contactOption": ["HearingImpairedSupported"]
}]
}
</script>
结论
嘿,看看!现在我们可以使用优化的数据增强网站的搜索引擎排名,这些数据允许搜索引擎以一种有组织的方式抓取和解释信息,从而促进更好的用户体验。
当然,此示例主要侧重于 JSON-LD、Google 的 Schema 规范以及使用 WordPress 作为管理和生成数据的工具。如果您已编写了使用其他格式、不同规范和其他内容管理系统管理和处理数据的方法,请在评论中分享,我们可以开始为全面改进 SEO 形成更宏观的认识。
精彩的回顾,
谢谢!
哇,非常感谢您深入探讨 Schema。
我最近一直在四处寻找,并认为一定有一些使用 ACF 的解决方案……但实际上并没有太多,无论是否使用 ACF。
很兴奋……会尽快尝试一下。:D
虽然我发现你的文章很有趣,并且一定会尝试一下,但我正在考虑可访问性和页面渲染问题。
使用屏幕阅读器的用户是否会利用这些微数据标签?我不确定,只是猜测。可能有一些原因导致微数据仍在开发和维护(我发现它比 JSON-LD 更易于管理)。
另外,如果你把那个脚本放在文档头部,它不会阻止网页渲染吗?
SEO 相关内容不一定与可访问性相关。据我所知,屏幕阅读器会忽略这些标签,主要关注 ARIA HTML 属性,这些属性的工作方式与 Schema 类似,但专门针对无障碍性而创建。
HTML5 Doctor 提供了一个有用的介绍,而 MDN 提供了关于该主题的概述和资源。
再见,w0lf。
如果你担心渲染阻塞,你可以使用操作钩子中的 `wp_footer` 将代码输出到页脚。
关于渲染阻塞,此代码应位于 head 部分,并且对于 AMP 页面等必须位于 head 部分。
这正是我在网站上想要实现的。我正在使用 WordPress 的主题,并且遇到了一些困难。如果我遇到问题,可以发邮件给你吗?
感谢你的帮助!
当然可以。
email(at)artofmyself.com
但我可能不熟悉你的问题,因为我根本不使用预制主题,但让我们看看。
太棒了,谢谢 Pascal!我昨天还在研究如何为用 WordPress 构建的餐厅网站做 Schema 标记,所以这对我是个完美的入门工具包 :)
我很想知道 João 关于可访问性和渲染阻塞的问题的答案。
Moz 有一篇关于餐厅的 Schema 文章,如果有人感兴趣。为菜单编写标记听起来……不太有趣。
你让我很好奇!
我终于第一次尝试了 ACF,真的很酷。
顺便说一句,我是 Schema 插件的开发者,我在寻找一篇好文章时偶然发现了这里。
我一直在思考这个问题!Schema 插件有一个内置的帖子元字段生成器,它允许你创建帖子元字段并自动使用其值来过滤标记输出。但是,当然……它不支持特殊类型的字段(例如:地址、营业时间……等)。
ACF(以及其他类似的插件)非常方便地填补了这些空白。我喜欢你如何组合地址字段。
因此,我决定尝试一下,并且很高兴……我能够用新的 ACF 生成器替换旧的帖子元生成器 :),现在是时候开发一些自定义字段并根据字段类型有条件地更改标记输出了。通过这种方式,我们不必编写任何额外的代码,好吧……如果这是完整的。
再次感谢你的好文章和灵感!
顺便说一句,Schema 插件做得很好
A) 作为“Schema 插件开发者”,我会考虑:学习并深入了解 ACF 的基础世界,并借鉴你发现的架构理念。所有“映射变量”的解决方案都可以通过 ACF 以非常漂亮的方式完成,并以此逻辑改进插件。但请确保你拥有带有重复器字段的专业版。
B) 尝试将其作为 ACF 扩展,Github 上有一些这样的项目,但它们已经消失了。但我可以看到 ACF 论坛中存在巨大的兴趣,因为我拥有开发者许可证。但请创建自己的重复器版本。或者查看他们的许可证协议,你被允许将代码的版本用作免费插件。
在过去的 6 个月里,我们一直在尝试为我们的项目寻找不同的 Schema 解决方案。我们测试了免费和付费插件。Schema 插件 (!) 是我们选择的通用解决方案,即使 WPSSO 拥有强大的功能 -即使 Schema 插件的“灵活字段构建过程”毫无用处。
嘿 Jonas,很高兴听到这个消息,非常感谢你的反馈。
我现在已经领先了,我获得了专业的 ACF 许可证来进行测试,它已经在我其中一个插件(扩展)中进行测试,并且进展良好。
这是一篇很棒的文章。不过我有一个问题。我的企业有 4 个地点,我正在尝试弄清楚如何处理这个问题。