メモログ

💡 Personal notes about somthing I'm interested in

Add JSON-LD structured data

構造化データ(structured data)の部分をmicrodataからJSON-LDに置き換えてみた。

とりあえずArticle | Search | Google Developersにある「SEE MARKUP」からサンプルのJSON-LDのデータをコピーして、それを元にhexoのテンプレートの個別のページのアーカイブに下記のJSON-LDを追加。

<script type="application/ld+json">
  {
    "@context": "http://schema.org",
    "@type": "BlogPosting",
    "mainEntityOfPage": {
      "@type": "WebPage",
      "@id": "<%- page.permalink %>"
    },
    "headline": "<%- page.title %>",
    <% if (page.featured && page.featured.image) { %>
      "image": [
        "https://memolog.org/assets/images/<%= page.featured.image %>/<%= page.featured.image %>.jpg"
      ],
    <% } %>
    "datePublished": "<%- page.date %>",
    "dateModified": "<%- page.updated %>",
    "author": {
      "@type": "Person",
      "name": "Yutaka Yamaguchi"
    },
    "publisher": {
      "@type": "Organization",
      "name": "メモログ",
      "logo": {
        "@type": "ImageObject",
        "url": "https://memolog.org/assets/icons/icon-1024.png"
      }
    },
    "description": "<%- remove_html(page.excerpt) %>"
  }
</script>

descriptionにはHTMLを含まないテキストを出力したいのだけど、hexoのpage.excerptの出力にはHTMLが含まれていてそれを取り除く手段が標準ではない(ように見える)。なので、remove_htmlという簡単なヘルパーを用意した。改行の情報などもいらないので、DOMにデータを入れてそれをtextContentでテキスト部分を取得するだけ。

const jsdom = require('jsdom');
const { JSDOM } = jsdom;

hexo.extend.helper.register('remove_html', function(content){
  return new JSDOM(`<div>${content}</div>`).window.document.getElementsByTagName('div')[0].textContent || '';
});

同様に、各ページの共通の情報としてLogoSocial Profileのデータを追加している。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@graph":
  [
    {
      "@context": "http://schema.org",
      "@type": "Organization",
      "url": "http://memolog.org",
      "logo": "https://memolog.org/assets/icons/icon-1024.png"
    },
    {
      "@context": "http://schema.org",
      "@type": "Person",
      "name": "Yutaka Yamaguchi",
      "url": "http://memolog.org",
      "sameAs": [
        "https://www.facebook.com/yutaka.yamaguchi",
        "https://www.linkedin.com/in/yutakayamaguchi/",
        "https://twitter.com/memolog"
      ]
    }
  ]
}
</script>

二つのトップレベルのアイテムを一つのscriptタグで追加したい場合は、@graphで記入すると良いとweb - JSON-LD Schema.org: Multiple video/image page - Stack Overflowに書かれてあったのでそのようにした(複数のscriptタグに分けてもいい)。

インデックステンプレートの方にはCarouselsの記述を入れることにした。

<script type="application/ld+json">
{
  "@context":"http://schema.org",
  "@type":"ItemList",
  "itemListElement":[
    <% page.posts.each( (post, i) => { %>
    {
      "@type":"ListItem",
      "position":<%- i+1 %>,
      "url": "<%- post.permalink %>"
    }<% if (i + 1 < page.posts.length ) { %>,<% } %>
    <% }) %>
  ]
}
</script>

Carouselの表示はMark Up Your Content Items には、「The Top stories carousel requires that your content be published in AMP. For more information, see AMP with structured data.」とあり、non-AMPページではいわゆるカルーセルでの表示ではなく、リスト表示になるようである。

ので、そのうちAMPにも対応したいなと思うけど、まあそのうち。

最後に構造化データ テストツールで表示を確認。実際の検索結果に出てくるかどうかはわからないけど、データ的には特に問題なさそうである。

以上。