はてなブログの記事の最終更新日を取得
はてなブログの記事の最終更新日を取得
はてなブログの記事では, 最終更新日がデフォでは表示されないので,取得して表示しようというメモ.
記事のhtmlの中には最終更新日が書かれていない
表示はしていないが,メタ情報としてhtmlに書いている場合もある(例えば,head
の中にmeta
として設定している場合など)のでhtmlを調べてみたが,
どうやら発行日時pubdate
のみで,modifieddate
といった最終更新日の情報はないようだ.
最終更新日を取得する方法
実際に取得する方法を検索したところ以下の2つの方法を発見した.
- はてなブログのGoogle AMPの設定を有効(はてなPro限定)にして最終更新日時を取得 psn.hatenablog.jp
- サイトマップの利用 www.tsubasa-note.blog
はてなProユーザでない場合,「サイトマップの利用」しかない.
サイトマップの利用
サイトマップは,基本的に検索エンジンにウェブページがいつ更新されたか教えるインデックスとなっている.
はてなブログの場合,サイトマップはblog_url/sitemap_index.xml
で確認できる.
このブログの場合:http://cartman0.hatenablog.com/sitemap_index.xml
サイトマップ1ファイルに100記事分のURLと最終更新日時(lastmod
)が保存されている.
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>http://cartman0.hatenablog.com/</loc> <lastmod>2017-11-08</lastmod> </url> <url> <loc>http://cartman0.hatenablog.com/about</loc> <lastmod>2017-11-08</lastmod> </url> <url> <loc> http://cartman0.hatenablog.com/entry/2017/11/07/schema.org%E3%81%AEArticle%E3%83%A1%E3%83%A2 </loc> <lastmod>2017-11-08</lastmod> </url> ....
コード
処理の流れ:
fetch
APIでサイトマップxmlを取得(このブログではこのファイルhttp://cartman0.hatenablog.com/sitemap_index.xml)- サイトマップxmlから各サイトマップのリスト(URL)の取得
- そのリスト(URL)に対して
fetch
APIで全XML取得(今回は,Promise.all()
を使って全リストに対して非同期処理で取得) - 全XMLから記事のURLを探索して,最終更新日を取得
- 最終更新日のDOMを生成して追加
(ちなみに,サイトマップは同ドメインにあるのでfetchなどのajax通信をしてもCORSに引っかからない.)
JavaScriptコード:gist
<!-- 更新日を追加 --> <script type="text/javascript"> (function(){ /* - args: lastmod: str */ function addLastmod(lastmod){ /* ここでlastmodを追加 */ var time = document.createElement("time"); time.setAttribute("itemprop", "dateModified"); time.setAttribute("title", lastmod); time.setAttribute("datetime", lastmod); time.innerText=lastmod; var span = document.createElement("span"); span.innerHTML = "(LastModified: " + time.outerHTML + ")"; var date_elements = document.querySelectorAll("div.date") Array.prototype.map.call(date_elements, function(e){e.appendChild(span);}); } var blog_uri = document.querySelector("[data-blog-uri]").getAttribute("data-blog-uri"); var sitemap_url = blog_uri + "sitemap.xml"; var article_url = document.querySelector('[property="og:url"]').getAttribute("content"); function fetch2str(res) { return res.text(); } function str2dom(str) { var parser = new DOMParser(); var dom = parser.parseFromString(str, 'text/xml'); return dom; } /* return : Promise([URL1, URL2, ...]) */ function getSitemapURLArrayPromiseFromSitemapXML() { return fetch(sitemap_url) .then(fetch2str) .then(str2dom) .then(function(dom) { return Array.prototype.map.call(dom.querySelectorAll("sitemap"), function(e) { return e.querySelector("loc").innerHTML }); }); } /* - return Promise([""<xml >?page=1...</xml>", "<xml >?page=2...</xml>", ...]) */ function getXMLStrArrayPromiseFromAllSitemapURL(URL_array) { return Promise.all(URL_array.map(function(e) { return fetch(e).then(fetch2str); })); } /* return Promise(url_element in DOMParser) */ function find_urlElementMatchedAtriclePromiseFromXMLStrArray(xml_str_arr) { var parser = new DOMParser(); var find_url_e = undefined; for (var xml_str of xml_str_arr) { var dom = parser.parseFromString(xml_str, 'text/xml'); var url_elements = dom.querySelectorAll("url"); find_url_e = Array.prototype.find.call(url_elements, function(e, idx, arr) { return e.querySelector("loc").innerHTML === article_url; }); if (find_url_e) break; } return find_url_e; } function getLastmodFrom_urlElement(url_element){ return url_element.querySelector("lastmod").innerHTML; } try{ getSitemapURLArrayPromiseFromSitemapXML() .then(getXMLStrArrayPromiseFromAllSitemapURL) .then(find_urlElementMatchedAtriclePromiseFromXMLStrArray) .then(getLastmodFrom_urlElement) .then(addLastmod); }catch(e){ console.log(e); } }()); </script>
以上のスクリプトを埋め込むと,以下のように,pubdate
の隣に追加される.
TODO
Promise.all()
を使って,100記事を格納している各XMLファイルを一括ですべて取得している- よって,記事が多いブログでは容量が必要になる場合がある.
- また,全取得なので対象がループの最初の記事でも処理時間は同じになる.
解決策1:非同期でやるならば,理想的には最初の成功だけ返す
Promise.some()
のような処理がほしい.- 解決策2:ループの最初の記事の処理を早くしたい場合,同期処理で解決できる.
fetch
APIの場合,非同期関数async
を宣言して中でwait
で同期処理ができる. ただし,現在,GoogleBotでは対応していない.