masalibの日記

システム開発、運用と猫の写真ブログです

はてなブログにArticleの構造化データを追加

経緯

google先生に更新日時、公開日時を伝えるといいよという記事をみた

www.suzukikenichi.com

検索結果にイメージが記載されるとクリック率があがるそうです。

f:id:masalib:20181220194633p:plain
リッチリザルトイメージ

https://developers.google.com/search/docs/data-types/article
より引用

はてなブログでもできないのかと思い調べた。

記事単位に設定できるものはなく、javascriptの非同期処理でできる事がわかった

cartman0.hatenablog.com

ただイメージは追加される時とされない場合がある。
正直・・・どれだけ効果があるのかわからない。でもSEO的にはよい事なので追加してみた

前提

はてなブログの記事には更新日時がない。 非同期で「amp」 or 「sitemap.xml」から取得するという荒業が必要。 非同期という所がちょっと怖いので今回は諦めて公開日時のみ設定した

cartman0.hatenablog.com

を参考につくっています

ちがう所は公開日時と更新日時を取得する部分です。うまく取れなかったり、違う日付がとれたので変えた。
(違う日付とはサイト全体の更新日時だった)

参考サイトのjavascript

var datePublished = "2014-04-30";
var dateModified = undefined || try_return(function(){return document.querySelector('.profile-activities [datetime]').getAttribute("datetime");});

自分があげたjavascript

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

手順

1. javascriptソースをローカルにもってくる

下記のソースをコピーしてテキストエディタにコピーする

クリックするとソースが展開されます

<!-- website json-ld fetch だと動かない関数がある? -->
<script type="text/javascript">
(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-blog-name]").getAttribute("data-blog-name");});
var uri = undefined || try_return(function(){return document.querySelector("[data-blog-uri]").getAttribute("data-blog-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");});
{return document.querySelector('.profile-activities [datetime]').getAttribute("datetime");});
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 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 genre = ["Web", "Technology"];
/*
...演算子はtry-catchの中でもエラーで止まる?ような挙動をするためfor-ofなどで代替する
*/
var hatena_genre = undefined || try_return(function(){
    var arr = [];
    for(var e of document.querySelectorAll('[data-circle-id]')){
        arr.push(e.getAttribute("title"));
    }
    return arr;
});
if(hatena_genre && hatena_genre.length > 0) genre = genre.concat(hatena_genre);

//★★★修正箇所★★★
var person_name = "masalib" || 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": "masalib@gmail.com"
};
if(person_name) person["name"] = person_name;
if(person_image) person["image"] = person_image;
var sponsor_organization = {
  "@type": "Organization",
  "name": "GoogleAdsense"
};
var search_action= !search_uri ? undefined : {
    "@type": "SearchAction",
    "target": search_uri + "?q={search_term_string}",
    "query-input": "required name=search_term_string" //google特有のプロパティ
};

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",
    "isAccessibleForFree": true
};
if(name) website_obj["name"] = name;
if(uri) website_obj["url"] = uri;
if(uri) website_obj["mainEntityOfPage"] = {"@type": "WebPage","@id": uri};
if(image) website_obj["image"] = image;
if(image) website_obj["thumbnailUrl"] = image;
if(description) website_obj["description"] = description;
if(keywords) website_obj["keywords"] = keywords;
if(charset) website_obj["encoding"] = {"@type": "MediaObject","encodingFormat": charset};
if(datePublished) website_obj["datePublished"] = datePublished;
if(dateModified) website_obj["dateModified"] = dateModified;
if(person) website_obj["author"] = person;
if(person) website_obj["publisher"] = person;
if(person) website_obj["copyrightHolder"] = person;
if(copyrightYear) website_obj["copyrightYear"] = copyrightYear;
if(inLanguage) website_obj["inLanguage"] = inLanguage;
if(genre) website_obj["genre"] = genre;
if(sponsor_organization) website_obj["sponsor"] = sponsor_organization;
if(search_action) website_obj["potentialAction"] = search_action;

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

2. personの配列のemailを修正

var person = {
    "@type": "Person", 
    "address": "Japan",
    "email": "masalib@gmail.com"
};

3. person_nameを修正

var person_name = "masalib"

4. デザインのヘッダー部分に埋め込む

バックアップをとってからやった方が安全です

f:id:masalib:20181219193500p:plain

5. 確認

chromeデベロッパーモードで出力されている事を確認する。 サイトトップやカテゴリ一覧以外は表示されます

f:id:masalib:20181220200237p:plain
構造化データ

{
    "@context": "http://schema.org",
    "@type": "WebSite",
    "fileFormat": "text/html",
    "isAccessibleForFree": true,
    "name": "masalibの日記",
    "url": "https://masalib.hatenablog.com/",
    "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": "https://masalib.hatenablog.com/"
    },
    "image": "https://cdn-ak.f.st-hatena.com/images/fotolife/m/masalib/20181219/20181219193603.png",
    "thumbnailUrl": "https://cdn-ak.f.st-hatena.com/images/fotolife/m/masalib/20181219/20181219193603.png",
    "description": "色々調べたが、はてなブログの記事には「更新日時」を表示させる独自タグなどがない。 「amp」 or 「sitemap.xml」から取得するという荒業が必要だった。 自分は「pro」なので「amp」方式にした psn.hatenablog.jp 参考にしたサイトのjavascriptをデザインのヘッダーに貼り付けるだけ!! はてなブログ編集のデザインのヘッダー パンくずの下に表示できた。 公開日時が追加 比較的簡単にできるがwordpressと比べると非常にめんどくさい。 追加できるように要望をした 以下、要望の内容 記事の作成日時(公開日)は表示されるのですが 記事の更新日時を表示するには ど…",
    "keywords": "php,ruby,インフラ,糖質制限,サーバーセットアップ,SQLServer,グーグルアナリティクス",
    "encoding": {
        "@type": "MediaObject",
        "encodingFormat": "utf-8"
    },
    "datePublished": "2018-12-19T15:00:00Z",
    "author": {
        "@type": "Person",
        "address": "Japan",
        "email": "masalib@gmail.com",
        "name": "masalib",
        "image": "https://cdn.profile-image.st-hatena.com/users/masalib/profile.png"
    },
    "publisher": {
        "@type": "Person",
        "address": "Japan",
        "email": "masalib@gmail.com",
        "name": "masalib",
        "image": "https://cdn.profile-image.st-hatena.com/users/masalib/profile.png"
    },
    "copyrightHolder": {
        "@type": "Person",
        "address": "Japan",
        "email": "masalib@gmail.com",
        "name": "masalib",
        "image": "https://cdn.profile-image.st-hatena.com/users/masalib/profile.png"
    },
    "copyrightYear": "2018",
    "inLanguage": [
        "ja",
        "en"
    ],
    "genre": [
        "Web",
        "Technology"
    ],
    "sponsor": {
        "@type": "Organization",
        "name": "GoogleAdsense"
    },
    "potentialAction": {
        "@type": "SearchAction",
        "target": "https://masalib.hatenablog.com/search?q={search_term_string}",
        "query-input": "required name=search_term_string"
    }
}

6. 構造化データになっているのか確認

https://search.google.com/structured-data/testing-tool?hl=ja

f:id:masalib:20181220200904j:plain
チェックツール画面

f:id:masalib:20181220200928j:plain
チェック結果画面

色々やったが。。。はてなブログ本体が対応してくれればこんなめんどくさい事をしなくてもいいんだけど。。。。