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

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

JavaScriptで動的にschema.orgのJSON-LDを埋め込む

JavaScriptで動的にschema.orgJSON-LDを埋め込む

JavavScirptでschema.orgJSON-LDを生成して,Google Botに読んでもらおうというメモ.

なぜJavaScirptを使うのか

手動でschema.orgJSON-LDを書いてもいいが,プロパティが多いと大変. しかし,CMSでないブログサービスのようなものの場合,テンプレートタグや独自タグなどのmeta要素を使用できない.

一方,JavaScriptの場合,ブログサービスでも動作する場合がある. なので,JavaScriptでWebページからプロパティに設定したい要素を取得し,schema.orgJSON-LDを生成・埋め込むということをする.

これを行うには,以下の2点が問題になる.

そもそもGoogleJavaScript実行後のページを読むのか

こちらの記事で解説している.

cartman0.hatenablog.com

GoogleBotでは,ES6のような新しい関数はエラーが出て処理が止まるが, ES6以前のものであれば処理を実行し見ている(Fetch As Googleで確認できる).

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

こちら記事では,JavaScriptを使って, はてなブログパンくずリストについて構造化データのマークアップをしている.

www.du-soleil.com

つまり,JavaScriptで生成した構造化データでもGoogleは認識する.

構造化データを生成するJavaScriptのポイント

  • htmlページの要素をプロパティとして取得するので,loadイベント後に実行する.
  • GoogleBotで処理できるJavaScriptの関数を使う.
  • エラーが出ると処理が止まってしまうので,1つのプロパティのための情報を取得するごとにtry-catchを使う.

例:WebSite

schema.orgのタイプ:WebSiteについて以下の記事にまとめた.

cartman0.hatenablog.com

以下のウェブサイトのhtmlを考える.

<html lang="ja" data-website-name="ウェブサイト名" data-website-uri="http://example.com/">
    <head>
        <meta charset="utf-8"/>
        <meta itemprop="image" content="https://example.com/images/image-1500.png" />
        <meta name="description" content="ウェブサイトの説明" />
        <meta name="keywords" content="keyword1, keyword2">
        <meta name="modified" content="2017-11-06" />
    </head>
    <body>
        ...
       <img class=".profile-icon" src="http://exmaple.com/profile.jpg" />
        ...
    </body>
</html>

JavaScriptコード: 各プロパティに対して, - 固定値を使いたい場合は,undefeindの部分を変更 - 動的に取得したい場合は,try_return関数のに渡す関数の処理を変更

という使い方にした.

取得するプロパティは,以下を想定.

  • ウェブサイトの名前
  • ウェブサイトのURL(potentialActionのSearchActionにも使用)
  • ウェブサイトの説明
  • ウェブサイトの画像
  • 発行日
  • 更新日
  • author用の画像
  • エンコード
  • 言語(htmlのlang)
(function(){
    function create_schemaorg_website(){
        function try_return(f){
            try{return f.call();}catch(e){}
        }

        /* setting */
        var name = undefined || try_return(function(){return document.querySelector("[data-website-name]").getAttribute("data-website-name");});
        var uri = undefined || try_return(function(){return document.querySelector("[data-website-uri]").getAttribute("data-website-uri");});
        var search_uri = undefined || (uri ? uri + "search" : undefined);
        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 keywords = undefined || try_return(function(){return document.querySelector('[name="keywords"]').getAttribute("content");});
        var charset = undefined || try_return(function(){return document.querySelector('[charset]').getAttribute("charset");});
        var datePublished = "2014-04-01";
        var copyrightYear = undefined || try_return(function(){return datePublished.match(/^(\d{4})-/)[1];});
        var dateModified = undefined || try_return(function(){return document.querySelector('[name="modified"]').getAttribute("modified");});
        var inLanguage = undefined || try_return(function(){return document.querySelector('[lang]').getAttribute("lang");});

        /* person */
        var person_name = "your name";
        var person_image = undefined || try_return(function(){return document.querySelector('.profile-icon').getAttribute("src");});
        var person = {
            "@type": "Person",
            "address": "Japan",
            "email": "mail@email.com"
        };
        if(person_name) person["name"] = person_name;
        if(person_image) person["image"] = person_image;

        var search_action= !search_uri ? undefined : {
            "@type": "SearchAction",
            "target": search_uri + "?q={search_term_string}",
            "query-input": "required name=search_term_string" // google特有のプロパティ
        };

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

        /* create website_json */
        var website_obj = {
            "@context": "http://schema.org",
            "@type": "WebSite",
            "fileFormat": "text/html",
        };
        if(name) website_obj["name"] = name; // name
        if(uri) website_obj["url"] = uri; // url
        if(uri) website_obj["mainEntityOfPage"] = {"@type": "WebPage","@id": uri}; // mainEntityOfPage
        if(image) website_obj["image"] = image; // image
        if(image) website_obj["thumbnailUrl"] = image; // thumbnailUrl
        if(description) website_obj["description"] = description; // description
        if(keywords) website_obj["keywords"] = keywords // keywords
        if(charset) website_obj["encoding"] = {"@type": "MediaObject","encodingFormat": charset}; // encoding
        if(datePublished) website_obj["datePublished"] = datePublished; // datePublished
        if(dateModified) website_obj["dateModified"] = dateModified; // dateModified
        if(person) website_obj["author"] = person; // author
        if(person) website_obj["publisher"] = person; // publisher
        if(person) website_obj["copyrightHolder"] = person; // copyrightHolder
        if(copyrightYear) website_obj["copyrightYear"] = copyrightYear; // copyrightYear
        if(inLanguage) website_obj["inLanguage"] = inLanguage; // inLanguage
        if(search_action) website_obj["potentialAction"] = search_action; // potentialAction

        script.innerText = JSON.stringify(website_obj);
        document.head.appendChild(script);
    }
    window.addEventListener("load", create_schemaorg_website, false);
}());

例:Article

schema.orgのタイプ:Article について以下の記事にまとめた.

cartman0.hatenablog.com

以下のようなブログの記事を考える.

取得するプロパティは,以下を想定.

  • 記事のタイトル
  • 記事のURL
  • 記事の説明
  • 記事の画像
  • 発行日
  • 更新日
  • author用の画像
  • publisher用のブログタイトル,url,ロゴ画像のurl
  • エンコード
  • 言語(htmlのlang)
<!DOCTYPE html>
<html lang="ja" data-blog-name="Blog Name" data-blog-uri="http://example.com/">
  <head>
    <meta charset="utf-8">
    <meta name="description" content="記事の説明">
    <meta itemprop="image" content="http://example.com/atritcles/article1/article_img.jpg">
  </head>

  <body>
    <article>
    <h1><a class="entry-title-link" href="http://example.com/atritcles/article1">記事タイトル</a></h1>
    <span>Published:<time pubdate datetime="2017-11-01">2017-11-01</time></span>
    <span>Modified:<time itemprop datetime="2017-11-07">2017-11-07</time></span>
    <p>記事本文</p>
    </article>

    <aside id="profile">
      <h3>プロフィール</h3>
      <img class="profile-icon" src="http://example.com/profile.jpg" >
    </aside>
  </body>
</html>

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");
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");});

/*person for author*/
var person_name = "user name" || 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": "exsample@example.com"
};
if(person_name) person["name"] = person_name;
if(person_image) person["image"] = person_image;

/* publisher info*/
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://exapmle.com/logo.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 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('[lang]').getAttribute("lang");});

/*script生成*/
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;

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