いしぐめも

プログラミングとかしたことを書きます。

はてなブログの記事にJSON-LDで構造化データを入れる

最近JSON-LDの勉強を始めて、どこかで実践できる場はないかな?ということで、自分のブログの記事ページに対して、JSON-LDで記述した情報を記載してみることにしました。

この内容は既に実践されている方がいて、以下の記事を大変参考にさせていただきました。

cartman0.hatenablog.com

リッチリザルトテスト

構造化データが正しくセットされているかどうかは以下の「リッチリザルトテスト」を行うことで確認できます。

Rich Results Test - Google Search Console

具体的には、指定したURLに対してGoogleクローラーが走り、必須項目などが正しくセットされているかを確認することができます。

記事ページにデフォルトで含まれるJSON-LD

とりあえずデフォルトでリッチリザルトテストを行ってみました。

f:id:yoh1496:20211028113703p:plain
デフォルトの記事ページに対してリッチリザルトテストを行った結果

すると、なんとデフォルトでもJSON-LDによる構造化データが含まれていることがわかりました。しかし以下のように警告が出てくるので見てみると、、、

f:id:yoh1496:20211028114154p:plain
デフォルトではauthorが指定されていないという警告

<script type="application/ld+json">
{
  "@context":"http://schema.org",
  "@type":"Article",
  "dateModified":"2021-09-13T18:32:07+09:00",
  "datePublished":"2021-09-10T20:47:53+09:00",
  "headline":"Personium上のファイルを操作(ダウンロード・アップロード・アクセス制御)してみよう",
  "image":["https://cdn-ak.f.st-hatena.com/images/fotolife/y/yoh1496/20210910/20210910200847.png"]
}
</script>

実際の記述がこちら(インデントは著者によるもの)。確かに、authorが含まれていません。

追加情報を記述するスクリプトを配置する

というわけで、基本的なデータははてなブログにデフォで記述されているものを踏襲するとして、足りていないauthorなどのデータを cartmanさんの記事 を参考に入れていきます。

スクリプト

cartmanさんの記事からちょっと修正し、すでにJSON-LDが含まれている場合はそれを上書きするようにしました。利用する際は AUTHOR_ADDRESS AUTHOR_NAME AUTHOR_URL を適宜変更してください。

<!-- Article JSON-LD -->
<script type="text/javascript">
  (function() {
    /* personal settings */
    var AUTHOR_ADDRESS = "Japan";
    var AUTHOR_NAME = "yoh1496";
    var AUTHOR_URL = "https://github.com/yoh1496";

    function create_schemaorg_article() {
      function tr(f) { try { return f.call(); } catch (e) { } }

      /* base json */
      var ae = document.querySelector('script[type="application/ld+json"]');
      if (!ae) return;

      var article_obj = Object.assign({
        "@context": "http://schema.org",
        "@type": "Article",
        "fileFormat": "text/html",
        "isAccessibleForFree": true,
      }, JSON.parse(ae.innerText));

      /* setting */
      var name = undefined || tr(function() { return document.querySelector("h1.entry-title").innerText; });
      var headline = undefined || tr(function() { return name.substr(0, 109); }); // [0, 110]まで
      var uri = undefined ||
        tr(function() { return document.querySelector(".entry-title-link").getAttribute("href"); }) ||
        tr(function() { return document.querySelector('[property="og:url"]').getAttribute("content"); });
      var image = undefined || tr(function() { return document.querySelector('[itemprop="image"]').getAttribute("content"); });
      var description = undefined || tr(function() { return document.querySelector('[name="description"]').getAttribute("content"); });
      var datePublished = undefined || tr(function() { return document.querySelector('[pubdate]').getAttribute("datetime"); });
      var dateModified = undefined || tr(function() { return document.querySelector("time[itemprop]").getAttribute("datetime"); });
      var person_image = undefined || tr(function() { return document.querySelector('.profile-icon').getAttribute("src"); });

      var person = {
        "@type": "Person",
        "address": AUTHOR_ADDRESS,
        "name": AUTHOR_NAME,
        "url": AUTHOR_URL,
      };
      if (person_image) person["image"] = person_image;

      var publisher_name = undefined || tr(function() { return document.querySelector("[data-blog-name]").getAttribute("data-blog-name"); });
      var publisher_url = undefined || tr(function() { return document.querySelector("[data-blog-uri]").getAttribute("data-blog-uri"); });
      var publisher_logo_image_url = "https://cdn.blog.st-hatena.com/images/theme/og-image-1500.png";
      var publisher = {
        "@type": "Organization"
      };
      if (publisher_name) publisher["name"] = publisher_name;
      if (publisher_url) publisher["url"] = publisher_url;
      if (publisher_logo_image_url) publisher["logo"] = {
        "@type": "ImageObject",
        "url": publisher_logo_image_url
      };

      var keywords = undefined || tr(function() {
        var arr = [];
        document.querySelectorAll(".entry-category-link").forEach(function(item) {
          arr.push(item.innerText);
        });
        return arr;
      });
      var genre = keywords;
      var charset = undefined || tr(function() { return document.querySelector('[charset]').getAttribute("charset"); });
      var copyrightYear = undefined || tr(function() { return datePublished.match(/^(\d{4})-/)[1]; });
      var inLanguage = undefined || tr(function() { return document.querySelector('[data-avail-langs]').getAttribute("data-avail-langs").split(" "); });

      // create article_json
      if (name) article_obj["name"] = name;
      if (headline) article_obj["headline"] = headline;
      if (uri) article_obj["url"] = uri;
      if (uri) article_obj["mainEntityOfPage"] = { "@type": "WebPage", "@id": uri };
      if (image) article_obj["image"] = image;
      if (image) article_obj["thumbnailUrl"] = image;
      if (description) article_obj["description"] = description;
      if (keywords) article_obj["keywords"] = keywords;
      if (charset) article_obj["encoding"] = { "@type": "MediaObject", "encodingFormat": charset };
      if (person) article_obj["author"] = person;
      if (publisher) article_obj["publisher"] = publisher;
      if (person) article_obj["copyrightHolder"] = person;
      if (copyrightYear) article_obj["copyrightYear"] = copyrightYear;
      if (datePublished) article_obj["datePublished"] = datePublished;
      if (dateModified) article_obj["dateModified"] = dateModified;
      if (inLanguage) article_obj["inLanguage"] = inLanguage;
      if (genre) article_obj["genre"] = genre;

      ae.innerText = JSON.stringify(article_obj);
    }
    window.addEventListener("load", create_schemaorg_article, false)
  }());
</script>

設定方法

上記スクリプトを配置する場所は 「設定」 - 「詳細設定」 とたどっていき…

f:id:yoh1496:20211028133406p:plain
ダッシュボードからの「設定」「詳細設定」をクリック

「要素にメタデータを追加」 のテキストエリアに上記スクリプトをコピペします。

f:id:yoh1496:20211028133543p:plain
<head> 要素にメタデータを追加

コピペしたら「保存する」ボタンで設定を適用してください。

確認方法

最初と同じようにリッチリザルトテストで結果を確認します。

f:id:yoh1496:20211028133908p:plain
警告が解消された!

終わりに

今回はブログにスクリプトを配置して構造化データを埋め込むということをやってみました。

スクリプトを書きながら「これじゃIEで動かないな~~」とか「スマホでも有効な配置場所にしなきゃなー」とか考えたんですけど、 クローラーに読ませる分には関係ないなと今更ながら思いました。(一応この手順たどればとりあえずリッチリザルトテストではモバイル版でも通るようになってます)

リンクトデータ自体はあまり身近にあるような感じがしなくてピンと来てなかったんですが、身の回りの情報に少しずつつけていくことでちょっと身近に感じられるようになる気がしました。