JavaScriptで動的にschema.orgのJSON-LDを埋め込む
JavaScriptで動的にschema.orgのJSON-LDを埋め込む
JavavScirptでschema.orgのJSON-LDを生成して,Google Botに読んでもらおうというメモ.
なぜJavaScirptを使うのか
手動でschema.orgのJSON-LDを書いてもいいが,プロパティが多いと大変. しかし,CMSでないブログサービスのようなものの場合,テンプレートタグや独自タグなどのmeta要素を使用できない.
一方,JavaScriptの場合,ブログサービスでも動作する場合がある. なので,JavaScriptでWebページからプロパティに設定したい要素を取得し,schema.orgのJSON-LDを生成・埋め込むということをする.
これを行うには,以下の2点が問題になる.
- GoogleがJavaScript実行後のページを読むのか
- JavaScriptで生成した構造化データをGoogleが認識するのか
そもそもGoogleはJavaScript実行後のページを読むのか
こちらの記事で解説している.
GoogleBotでは,ES6のような新しい関数はエラーが出て処理が止まるが, ES6以前のものであれば処理を実行し見ている(Fetch As Googleで確認できる).
JavaScriptで生成した構造化データをGoogleが認識するのか
こちら記事では,JavaScriptを使って, はてなブログのパンくずリストについて構造化データのマークアップをしている.
つまり,JavaScriptで生成した構造化データでもGoogleは認識する.
構造化データを生成するJavaScriptのポイント
- htmlページの要素をプロパティとして取得するので,
load
イベント後に実行する. - GoogleBotで処理できるJavaScriptの関数を使う.
- エラーが出ると処理が止まってしまうので,1つのプロパティのための情報を取得するごとにtry-catchを使う.
例:WebSite
schema.orgのタイプ:WebSiteについて以下の記事にまとめた.
以下のウェブサイトの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 について以下の記事にまとめた.
以下のようなブログの記事を考える.
取得するプロパティは,以下を想定.
- 記事のタイトル
- 記事の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>