はしくれエンジニアもどきのメモ

情報系技術・哲学・デザインなどの勉強メモ・備忘録です。

はてなブログに動的にschema.orgのArticleを埋め込む

はてなブログに動的にschema.orgのArticleを埋め込む

はてなブログJavaScriptで動的にschema.orgのWebSiteを埋め込むメモ.形式は,JSON-LDである.

以下の記事で, 一般的なwebサイトにschema.orgArticleを埋め込むスクリプトを書いたので,今回ははてなブログ用のものを書く.

cartman0.hatenablog.com

はてなブログ記事にデフォで設定されている構造化データ

Googleの構造化データテストツールをしてみるとわかるが,WebSite同様,記事にも構造化データ(形式はmicrodata)が設定してある. プロパティの'name'と'image'は設定されているが他は設定されていない.

  • name
  • image
  • author(必須)
  • datePublished(必須)
  • headline(必須)
  • publisher(必須)
  • dateModified(推奨)
  • mainEntityOfPage(推奨)

はてなブログの記事にデフォで設定されるArticleのプロパティ例

注意

はてなブログでは,すでに schema.orgのタイプArticleが設定してある.なので,今回のスクリプトを埋め込むと,microdataでデフォで設定されたArticleJSON-LDのArticleが混在するので注意.

schma.orgのArticleについて

以下の記事で解説している.

cartman0.hatenablog.com

JavaScriptで生成した構造化データをGoogleが認識するのか

以下の記事で解説

cartman0.hatenablog.com

はてなブログ記事のメタ情報

動的にJSON-LDを生成するために, はてなブログ記事ページのhtmlから各メタ情報を抽出する. ソースから確認できる.

  • ブログ名:
    <html ..
    data-blog-name="はしくれエンジニアもどきのメモ"
    ...>
    
  • 記事名
    <h1 class="entry-title">
     <a href="http://..." class="entry-title-link bookmark">はてなブログに動的にschema.orgのWebSiteを埋め込む</a>
    </h1>
    
  • 記事のURL
     <a href="http://cartman0.hatenablog.com/entry/2017/11/09/%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%96%E3%83%AD%E3%82%B0%E3%81%AB%E5%8B%95%E7%9A%84%E3%81%ABschema.org%E3%81%AEWebSite%E3%82%92%E5%9F%8B%E3%82%81%E8%BE%BC%E3%82%80" class="entry-title-link bookmark">はてなブログに動的にschema.orgのWebSiteを埋め込む</a>
    
  • 記事の画像
    <meta itemprop="image" content="https://cdn.blog.st-hatena.com/images/theme/og-image-1500.png"/>
    
  • 記事の説明
     <meta name="description" content="はてなブログに動的にschema.orgのWebSiteを埋め込む はてなブログにJavaScriptで動的にschema.orgのWebSiteを埋め込むメモ. …" />
    
  • 記事の発行日
    <time pubdate datetime="2017-11-09T13:54:29Z" title="2017-11-09T13:54:29Z">
    
  • 記事の最終更新日 はてなブログではデフォで取得できないので, 独自に設定したものを取得する. 詳細は,以下の記事に書いている. cartman0.hatenablog.comJavaScript実行後に取得)
    <span>(LastModified: <time itemprop="dateModified" title="2017-11-09" datetime="2017-11-09">2017-11-09</time>)</span>
    
  • charset
     <meta charset="utf-8"/>
    
  • 利用している言語
    <html ...
    data-avail-langs="ja en"
    ...
    >
    
  • 記事のカテゴリ
    <div class="entry-categories categories">
    <a href="http://cartman0.hatenablog.com/archive/category/%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%96%E3%83%AD%E3%82%B0" class="entry-category-link category-はてなブログ">はてなブログ</a>
    
    <a href="http://cartman0.hatenablog.com/archive/category/StructuredData" class="entry-category-link category-StructuredData">StructuredData</a>
    
    <a href="http://cartman0.hatenablog.com/archive/category/schema.org" class="entry-category-link category-schema.org">schema.org</a>
    </div>
    
  • ブログ投稿者のニックネーム (JavaScript実行後)
    <span data-load-nickname="1" data-user-name="cartman0"><span class="user-name-nickname">Cartman</span> <span class="user-name-paren">(</span><span class="user-name-hatena-id">id:cartman0</span><span class="user-name-paren">)</span></span>
    
  • ブログ投稿者のアイコン(プロフィールアイコン)
    <a href="http://cartman0.hatenablog.com/about" class="profile-icon-link">
          <img src="https://cdn1.www.st-hatena.com/users/ca/cartman0/profile.gif?1428245168"
          alt="id:cartman0" class="profile-icon" />
    </a>
    
  • ブログ名
    <html ..
    data-blog-name="はしくれエンジニアもどきのメモ"
    ...>
    
  • ブログのURL
    <html ...
    data-blog-uri="http://cartman0.hatenablog.com/"
    ...
    
  • ブログのロゴ画像 今回は,ブログのロゴ画像は直接指定
    https://cdn.blog.st-hatena.com/images/theme/og-image-1500.png"
    

JavaScriptコード

今回,Articleに設定するプロパティは,以下とする.

  • name: 記事名
  • url: 記事のURL
  • mainEntityOfPageArticleは,記事ページのメインコンテンツより,記事のURLを指定
  • description: 記事の説明
  • image:記事の画像
  • thumbnailUrl: 記事用のサムネイル画像(今回はimageと同じ)
  • keyword:記事のキーワード(今回はカテゴリ)
  • charset: htmlのcharset(つまりutf-8
  • "fileFormat": htmlページなので"text/html"
  • datePublished: 記事発行日
  • dateModified: 記事最終更新日(今回は事前に独自で設定したものを使用)
  • inLanguage:利用言語
  • genre: ジャンル(今回は記事のカテゴリを指定)
  • author: 記事の著者(ブログ発信者の名前とプロフィールアイコン,メールアドレス,アドレス)
  • publisher: 記事の発行者,今回はOrganizationとしてブログを指定(ブログのタイトル,URL,ロゴ画像を指定).
  • isAccessibleForFree: 無料でアクセスできるのでtrue
  • copyrightHolder: 記事の著作権者(今回はauthorと同じ.)
  • copyrightYear: 著作権の年(今回はdatePublishedの年を指定)
  • sponsor: 記事のスポンサー(今回は試しにGoogleAdsenseを指定)
<!-- Article JSON-LD -->
<script type="text/javascript">
(function(){
function create_schemaorg_article(){
  function try_return(f){
    try{
        return f.call();
    }catch(e){
    }
  }
  /* setting */
var name = undefined || try_return(function(){return document.querySelector(".entry-title-link").innerText;});
var headline = undefined || name.substr(0,109); // [0, 110]まで
var uri = undefined|| try_return(function(){return document.querySelector(".entry-title-link").getAttribute("href") || document.querySelector('[property="og:url"]').getAttribute("content");});
var image = undefined || try_return(function(){return document.querySelector('[itemprop="image"]').getAttribute("content");});
var description = undefined || try_return(function(){return document.querySelector('[name="description"]').getAttribute("content");});
var datePublished = undefined || try_return(function(){return document.querySelector('[pubdate]').getAttribute("datetime");});
var dateModified = undefined || try_return(function(){return document.querySelector("time[itemprop]").getAttribute("datetime");});
var person_name = "nabana" || try_return(function(){return document.querySelector('.user-name-nickname').innerText;});
var person_image = undefined || try_return(function(){return document.querySelector('.profile-icon').getAttribute("src");});
var person = {
    "@type": "Person",
    "address": "Japan",
    "email": "@mail.com"
};
if(person_name) person["name"] = person_name;
if(person_image) person["image"] = person_image;

var publisher_name = undefined || try_return(function(){return document.querySelector("[data-blog-name]").getAttribute("data-blog-name");});
var publisher_url = undefined || try_return(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 || try_return(function(){
    var arr = [];
    for(var e of document.querySelectorAll(".entry-category-link")){
      arr.push(e.innerText);
    }
    return arr;
});
var genre = keywords;
var charset = undefined || try_return(function(){return document.querySelector('[charset]').getAttribute("charset");});
var copyrightYear = undefined || try_return(function(){return datePublished.match(/^(\d{4})-/)[1];});
var inLanguage = undefined || try_return(function(){return document.querySelector('[data-avail-langs]').getAttribute("data-avail-langs").split(" ");});
var sponsor_organization = {
    "@type": "Organization",
    "name": "GoogleAdsense"
};

var script = document.createElement("script");
script.setAttribute("type", "application/ld+json");

// create article_json
var article_obj = {
    "@context": "http://schema.org",
    "@type": "Article",
    "fileFormat": "text/html",
    "isAccessibleForFree": true,
};
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;
if(sponsor_organization) article_obj["sponsor"] = sponsor_organization;

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

上のスクリプトはてなブログの「記事上」または「記事下」に埋め込んで,記事ページを「Googleの構造化データテストツール」にかけると以下のように認識される.

テスト結果:

実際に埋め込まれたArticle