Mark up articles with structured data

最近Wordpressのテーマを久しぶりに書いてみたのですが、Googleウェブマスターツールで構造化データのErrorやWarningが検出されてしまいました。。構造化データについては、scheme.orgBlogPostingというタイプを利用してMicrodata形式でプロパティ指定を行なっていたのですが、Googleサーチのガイドラインに沿わない部分もあったためかErrorやWarningが報告されていたのかなと思われます(SEOや検索エンジンへの影響については不明です)。

microdata-error

そこで、Googleのドキュメントから現時点でのガイドラインを確認しつつ、いくつかサイトへ組み込みそうなData Typeをピックアップして例としてまとめたいと思います。なお、ご自身のサイトへ適用した際の影響については十分ご確認の上ご自身の責任で判断ください。

Site Structure

Your Site Name in Results

Googleの検索結果で表示される名前を指定するために使います。ガイドラインとしてざっくりと以下のようなことが示されています。

  • 指定される名前はサイトドメインをもっともらしく表したものであること
  • ナチュラルな名前を使うこと。例)×:debug-life.net、○:debuglife
  • ユニークな名前であること
  • サイトの説明が不適切であってはならない
Property Description Required?
url サイトの公式なURL yes
name サイトの名前 yes
alternateName サイトの別名 no

上記をふまえてJSON-LD及びMicrodataフォーマットで書くと以下のようになるでしょうか(例は、WordPressを想定しています)。

{
  "@context": "http://schema.org",
  "@type": "WebSite",
  "name": "DebugLife",
  "url": "http://debug-life.net/"
}

WordPress Sample of JSON-LD

add_action('wp_footer', function() {
    if (is_home() && is_front_page()) {
        $json_ld = json_encode(array(
            "@context" => "http://schema.org",
            "@type"    => "WebSite",
            "name"     => get_bloginfo('name', 'display'),
            //"alternateName" => get_bloginfo('name', 'display'),
            "url"      => get_bloginfo('url', 'display')
        ));
        echo sprintf('<script type="application/ld+json">%s</script>', $json_ld);
    }
});
<head itemscope itemtype="http://schema.org/WebSite">
<title itemprop='name'>DebugLife</title>
<link rel="canonical" href="http://debug-life.net/" itemprop="url">

WordPress Sample of Microdata

add_action('wp_head', function() {
    if (is_home() && is_front_page()) {
        echo sprintf(<<<EOS
<head itemscope itemtype="http://schema.org/WebSite">
<title itemprop='name'>%s</title>
<link rel="canonical" href="%s" itemprop="url"> 
EOS
        , esc_html(get_bloginfo('name', 'display')), esc_url(get_bloginfo('url', 'display')));
    } else if (is_singular()) {
        echo sprintf(<<<EOS
<title>%s</title>
<link rel="canonical" href="%s"> 
EOS
        , esc_html(get_bloginfo('name', 'display')), esc_url(get_permalink()));
    } else {
        echo sprintf(<<<EOS
<title>%s</title>
EOS
        , esc_html(get_bloginfo('name', 'display')));       
    }
});

参考資料

  • https://developers.google.com/search/docs/data-types/sitename

Breadcrumbs

Webページの階層を示すのに使います。いわゆるパンくずリストです。image属性が必須となっており悩んでしまいます。カテゴリ用の画像やiconを準備しておく必要があるのかもしれません。

Property Description Required?
image 階層ページの画像URL yes
item ページのID yes
name ユーザーに表示されるページの名前 yes
position ページの階層位置。1から数える yes
{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [{
    "@type": "ListItem",
    "position": 1,
    "item": {
      "@id": "http://debug-life.net/",
      "name": "Home",
      "image": "http://debug-life.net/logo.png"
    }
  },{
    "@type": "ListItem",
    "position": 2,
    "item": {
      "@id": "http://debug-life.net/entry/category/html",
      "name": "Html",
      "image": "http://debug-life.net/html-icon.png"
    }
  }]
}

WordPress Sample of JSON-LD

function get_category_tree()
{
    $categories = array();
    $cats = get_the_category();
    while ($cat = array_shift($cats)) {
        $categories[$cat->cat_ID] = $cat;
        while ($cat && ($parent = $cat->parent) != 0) {
            if ($cat = get_category($parent)) {
                $categories[$cat->cat_ID] = $cat;
            }
        }
    }
    ksort($categories);
    return $categories;
}

add_action('wp_footer', function() {
    $items = array();
    $jdon_ld = array(
        "@context" => "http://schema.org",
        "@type"    => "BreadcrumbList",
    );
    $pos = 1;
    $items[] = array(
        "@type" => "ListItem",
        "position" => $pos++,
        "item" => array(
            "@id"   => get_bloginfo('url', 'display'),
            "name"  => get_bloginfo('name', 'display'),
            "image" => 'http://debug-life.net/logo.png'
        )
    );

    if (empty($categories = get_category_tree())) {
        goto ECHO_JSON_LD;
    }

    $keys = array_keys($categories);
    for ($i = 0, $size = count($keys); $i < $size; $i++, $pos = $pos + $i) {
        $cat = $categories[$keys[$i]];
        $images = get_posts(array('post_type' => 'attachment', 'post_status' => 'published', 'posts_per_page' => 1, 'attachement' => $cat->name));
        $image = current($images);
        $items[] = array(
            "@type" => "ListItem",
            "position" => $pos,
            "item" => array(
                "@id"   => get_category_link($cat),
                "name"  => $cat->name,
                "image" => $image->guid
            ),
        );
    }

ECHO_JSON_LD:
    echo sprintf('<script type="application/ld+json">%s</script>', json_encode(array(
            "@context" => "http://schema.org",
            "@type"    => "BreadcrumbList",
            "itemListElement" => $items
    )));
});
<ol>
  <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
    <a itemscope itemtype="http://schema.org/Thing" itemprop="item" href="http://debug-life.net">
        <span itemprop="name">Home</span>
        <img itemprop="image" src="http://debug-life.net/logo.png" alt="Home"/></a>
        <meta itemprop="position" content="1" />
  </li>
      ›
  <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
    <a itemscope itemtype="http://schema.org/Thing" itemprop="item" href="http://debug-life.net/entry/category/html">
      <span itemprop="name">html</span>
      <img itemprop="image" src="http://debug-life.net/wp-content/uploads/2016/05/debuglife-3.png" alt="html"/></a>
    <meta itemprop="position" content="2" />
  </li>
</ol>

WordPress Sample of Microdata

<?php
function get_category_tree()
{
    $categories = array();
    $cats = get_the_category();
    while ($cat = array_shift($cats)) {
        $categories[$cat->cat_ID] = $cat;
        while ($cat && ($parent = $cat->parent) != 0) {
            if ($cat = get_category($parent)) {
                $categories[$cat->cat_ID] = $cat;
            }
        }
    }
    ksort($categories);
    return $categories;
}
?>
<ol>
  <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
    <a itemscope itemtype="http://schema.org/Thing" itemprop="item" href="<?php bloginfo('url') ?>">
        <span itemprop="name">Home</span>
        <img itemprop="image" src="<?php bloginfo('url') ?>/logo.png" alt="Home"/></a>
        <meta itemprop="position" content="1" />
  </li>
  <?php if (!empty($categories = get_category_tree())): ?>
  <?php $pos = 1;
        foreach ($categories as $cat_id => $cat):
            $pos++;
            $images = get_posts(array('post_type' => 'attachment', 'post_status' => 'published', 'posts_per_page' => 1, 'attachement' => $cat->name));
            $image = current($images);
            ?>
  ›
  <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
    <a itemscope itemtype="http://schema.org/Thing" itemprop="item" href="<?php echo get_category_link($cat) ?>">
      <span itemprop="name"><?php echo esc_html($cat->name) ?></span>
      <img itemprop="image" src="<?php echo esc_url($image->guid) ?>" alt="<?php echo esc_attr($cat->name) ?>"/></a>
    <meta itemprop="position" content="<?php echo $pos ?>" />
  </li>
  <?php endforeach ?>
  <?php endif ?>
</ol>

Creative Works

Articles

記事のマークアップを構造化するのに使われます。これが結構必須項目が多くて大変です。

Property Description Required?
mainEntityOfPage 記事のcanonical URL recommended
headline タイトル、110文字以内 yes
image 記事の画像 yes
image.url 画像URL yes
image.width 画像幅 px yes
image.height 画像高さ px yes
publisher 記事の公開者(組織) yes
publisher.name 公開者名 yes
publisher.logo ロゴ(※ 縦横比に注意、width <= 600pxでheight 60px) yes
publisher.logo.url ロゴのURL yes
publisher.logo.width ロゴの幅 px ( 600px以内 ※) yes
publisher.logo.height ロゴの高さ px ( 60px以内 ※) yes
datePublished 記事の公開日 ISO8601 yes
dateModified 記事の最終更新日 ISO8601 recommended
author 記事の著者 yes
author.name 著者の名前 yes
description 記事の説明 recommended
{
  "@context": "http://debug-life.net",
  "@type": "NewsArticle",
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "http://debug-life.net/entry/379"
  },
  "headline": "Mark up articles with structured data",
  "image": {
    "@type": "ImageObject",
    "url": "http://debug-life.net/thumbnail/picture.png",
    "width": 700,
    "height": 400
  },
  "datePublished": "2016-06-08T08:00:00+09:00",
  "dateModified": "2016-06-08T13:39:00+09:00",
  "author": {
    "@type": "Person",
    "name": "debuglife"
  },
   "publisher": {
    "@type": "Organization",
    "name": "DebugLife",
    "logo": {
      "@type": "ImageObject",
      "url": "http://debug-life.net/wp-content/uploads/2016/06/picture.png",
      "width": 600,
      "height": 60
    }
  },
  "description": "HTMLの構造化について"
}

WordPress Sample of JSON-LD

global $post;
$thumbnail = get_post_thumbnail_id($post);
$image = array(get_template_directory_uri() . '/img/no-image.png', 600, 270);
if (!empty($thumbnail)) {
    if ($image_src = wp_get_attachment_image_src($thumbnail, 'full')) {
        $image = $image_src;
    }
}

$logo = array("http://debug-life.net/logo.png", 600, 60);

echo json_encode(array(
    '@context' => 'http://schema.org', 
    '@type'    => 'NewsArticle', 
    'mainEntityOfPage' => array(
        '@type' => 'WebPage',
        '@id'   => get_the_permalink($post),
    ),
    'headline' => get_the_title($post),
    'image' => array(
        '@type'  => 'ImageObject', 
        'url'    => $image[0],
        'width'  => $image[1],
        'height' => $image[2],
    ),
    'datePublished' => get_the_date(DATE_ISO8601, $post),
    'dateModified'  => get_post_modified_time(DATE_ISO8601, $post),
    'author' => array(
        'type' => 'Person',
        'name' => get_the_author_meta('nickname', $post->post_author),
    ),
    'publisher' => array(
        '@type' => 'Organization',
        'name'  => get_bloginfo('name', 'display'),
        'logo' => array(
            '@type'  =>  'ImageObject',
            'url'    =>  $logo[0],
            'width'  =>  $logo[1],
            'height' =>  $logo[2],
        )
    ),
    'description' => ($post->post_excerpt && strlen($post->post_excerpt) > 0) ?
        get_the_excerpt($post): trim(mb_substr(strip_tags($post->post_content), 0, 70)),
));
<div itemscope itemtype="http://schema.org/NewsArticle">
  <meta itemscope itemprop="mainEntityOfPage" itemType="https://schema.org/WebPage" itemid="http://debug-life.net/entry/379"/>
  <h2 itemprop="headline">Mark up articles with structured data</h2>
  <h3 itemprop="author" itemscope itemtype="https://schema.org/Person">
    By <span itemprop="name">debuglife</span>
  </h3>
  <span itemprop="description">最近Wordpressのテーマを久しぶりに書いてみたのですが、Googleウェブマスターツールで構造化データのErrorやWarningが検出されてしまいました</span>
  <div itemprop="image" itemscope itemtype="https://schema.org/ImageObject">
    <img src="http://debug-life.net/wp-content/uploads/2016/06/HTML5_sticker.png"/>
    <meta itemprop="url" content="http://debug-life.net/wp-content/uploads/2016/06/HTML5_sticker.png">
    <meta itemprop="width" content="1800">
    <meta itemprop="height" content="900">
  </div>
  <div itemprop="publisher" itemscope itemtype="https://schema.org/Organization">
    <div itemprop="logo" itemscope itemtype="https://schema.org/ImageObject">
      <img src="http://debug-life.net/wp-content/uploads/2016/05/debuglife.png"/>
      <meta itemprop="url" content="http://debug-life.net/wp-content/uploads/2016/05/debuglife.png">
      <meta itemprop="width" content="600">
      <meta itemprop="height" content="60">
    </div>
    <meta itemprop="name" content="DebugLife">
  </div>
  <meta itemprop="datePublished" content="2016-06-09T08:00:00+08:00"/>
  <meta itemprop="dateModified" content="2016-06-10T09:20:00+08:00"/>
</div>

WordPress Sample of Microdata

<?php
global $post;
$thumbnail = get_post_thumbnail_id($post);
$image = wp_get_attachment_image_src($thumbnail, 'full');
$logo = array("http://debug-life.net/logo.png", 600, 60);
?>
<div itemscope itemtype="http://schema.org/NewsArticle">
    <meta itemscope itemprop="mainEntityOfPage"  itemType="https://schema.org/WebPage" itemid="<?php the_permalink($post) ?>"/>
  <h2 itemprop="headline"><?php echo esc_html(get_the_title($post)) ?></h2>
  <h3 itemprop="author" itemscope itemtype="https://schema.org/Person">
    By <span itemprop="name"><?php the_author() ?></span>
  </h3>
  <span itemprop="description"><?php the_excerpt() ?></span>
  <div itemprop="image" itemscope itemtype="https://schema.org/ImageObject">
    <img src="<?php echo esc_url($image[0]) ?>"/>
    <meta itemprop="url" content="<?php echo esc_url($image[0]) ?>">
    <meta itemprop="width" content="<?php echo $image[1] ?>">
    <meta itemprop="height" content="<?php echo $image[2] ?>">
  </div>
  <div itemprop="publisher" itemscope itemtype="https://schema.org/Organization">
    <div itemprop="logo" itemscope itemtype="https://schema.org/ImageObject">
      <img src="<?php echo esc_url($logo[0]) ?>"/>
      <meta itemprop="url" content="<?php echo esc_url($logo[0]) ?>">
      <meta itemprop="width" content="<?php echo $logo[1] ?>">
      <meta itemprop="height" content="<?php echo $logo[2] ?>">
    </div>
    <meta itemprop="name" content="<?php bloginfo('name', 'display') ?>">
  </div>
  <meta itemprop="datePublished" content="<?php echo get_the_date(DATE_ISO8601, $post) ?>"/>
  <meta itemprop="dateModified" content="<?php echo get_post_modified_time(DATE_ISO8601, $post) ?>"/>
</div>

参考資料

  • https://developers.google.com/search/docs/data-types/articles

参考リンク

  • https://schema.org/
  • https://developers.google.com/search/docs/data-types/data-type-selector

byebyehaikikyou

日記やIT系関連のネタ、WordPressに関することなど様々な事柄を書き付けた雑記です。ITエンジニア経験があるのでプログラミングに関することなどが多いです。

シェアする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

コメントする