近頃はStatic Site Generator(静的サイトジェネレーター)が充実しています。企業の技術ブログや個人のブログ、一部のサイトに限ってはWordpressを代表するようなCMSを使わないケースも増えてきました。

 develop or drink ?  photo by tea ©

こちらのブログも静的サイトジェネレーターの1つであるHexoを使っています。

実録。WordpressからHexoにブログ移行して分かったメリット・デメリット

静的コンテンツで関連記事や人気記事を表示させる

本題ですが、静的サイトジェネレータのHexoで関連記事や人気記事を表示させるプラグイン「hexo-related-popular-posts」を作りました。Wordpressではお馴染みのアレです(笑)

今回はその導入方法と使い方を解説していきたいと思います。このプラグインを使用した表示例は本記事の「一緒に読まれている記事」「人気の記事」をご覧ください。

Pull Requestやissueも歓迎しておりますので、煮るなり焼くなりご自由にお使いくださいませ(^^)

目次

何を行っているのか

Hexoは、前述している通り静的サイトジェネレータと呼ばれる生成ツールです。

静的サイトジェネレータとは、htmlやjavascriptのようなフロントエンドで表示する静的コンテンツのみで構築されたwebサイトを生成するプログラムのことです。Wordpressと大きく異なり、動的なページが生成できるphpのようなバックエンドのサーバープログラムが使えません。

つまり、ブログ側でデータベースにアクセス数を記録したり、コメントを管理したりする機能は使えないのです。

そこで、Hexo向けに作った「hexo-related-popular-posts」では、静的ページの生成前に、記事に含まれるタグや、記事の本文、更にはGoogleアナリティクスから得たページビュー数を見にいきます。その情報から関連性の高い記事を選び出し、関連記事人気記事となる候補のリンクページを生成します。

関連記事はタグの共通数が多い記事を優先して表示します。共通数が同じ記事は記事本文のキーワード(名詞)が関連している数や、ページビュー数を元にして、順位を決めることにしました。タグの共通数から関連記事を決定する方法はnkmk/hexo-list-related-postsを参考にさせていただきました。

できないこと ←できた

記事本文を元に関連記事を表示する「形態素解析」はめんどうなので導入していません。個人的にはタグの関連度だけでも抽出精度としては十分かなと思ってますが、いかがでしょうか?もし差し支えなければご意見をお聞かせ下さい。

2016.11 形態素解析を導入しました

新たにコンテンツに含まれるキーワード(名詞)を抽出して、関連性が高い記事を選ぶ機能を追加しました。また、編集していない古い記事が何度も解析されないように、キャッシュ機能を追加しました。キャッシュ機能により、関連記事の生成スピードが高速になります。形態素解析は@takuya_aさんのkuromoji.jsを利用させていただきました。素晴らしいプラグインをありがとうございます!

それでは、続いてプラグインのインストールと使い方について説明していきたいと思います。

Ads

インストール方法

インストールはnode.jsではお馴染みのnpmからできます。

bash
1
$ npm install hexo-related-popular-posts --save

簡単な使い方 (デフォルト:関連記事5件)

プラグインのインストール後、テンプレート内にヘルパータグpopular_posts()を埋め込みます。

themes/your-theme/layout/path-to-your-article.ejs
1
<%-
  popular_posts()
%>

デプロイ

問題なければ、いつも通りHexoでデプロイしていきます。

bash
1
2
3
4
$ hexo clean
$ hexo s
$ hexo g
$ hexo d -g

以上の手順で、テンプレートに埋め込んだ箇所に関連記事のリンクが5件表示されます。

詳細機能の概要

先ほどは、簡単な使い方を説明しました。さらに詳細な設定として以下の機能が用意されています。

詳しい使い方は、引き続き以下をご覧ください。

ヘルパータグの表示オプション

ヘルパータグpopular_posts()はオプションを指定できます。例えば以下の記述例では、関連する記事をアイキャッチ画像付きで5件表示させています。表示例は本記事の「一緒に読まれている記事」をご覧ください。

themes/your-theme/layout/path-to-your-article.ejs
1
<%-
  popular_posts({ maxCount: 5 , ulClass: 'popular-posts' , PPMixingRate: 0.0 , isDate: false , isImage: true , isExcerpt: false})
%>

指定可能なオプションの説明は以下をご覧ください。

オプション 説明 デフォルト値
maxCount 表示件数 5
ulClass 生成されるリストのクラス名 'popular-posts'
PPMixingRate 人気記事と関連記事の混合割合。0.0(関連記事のみ)〜1.0(人気記事のみ)の範囲で指定可能。 0.0(=関連記事のみ)
isDate 日付をリストに含めるか? false(=非表示)
isImage 画像をリストに含めるか? false(=非表示)
isExcerpt 記事概要をリストに含めるか? false(=非表示)
PPCategoryFilter 人気記事で表示したいカテゴリを絞る undefined(=絞り込み無し)

人気記事の表示

人気記事の表示は、ヘルパータグpopular_posts()PPMixingRate : 1.0を指定します。

themes/your-theme/layout/path-to-your-article.ejs
1
<%-
  popular_posts({ PPMixingRate: 1.0 })
%>

人気記事の表示にはGoogle Analyticsのページビュー数を予め取得し、その情報を集計結果として利用しています。人気記事の表示にはGoogle Analytics APIのアカウントが必要になりますので、予めAPIのアカウント取得をしてください。

APIアカウント取得

hexo-related-popular-postsはga-analyticsを使いGoogle Analyticsにアクセスしています。Google Analytics API アカウントの取得方法は以下の記事が大変参考になりますので、ご覧ください。

情報アイランド
Google Analytics APIにアクセスできるようにするまで | 情報アイランド

アカウント情報を設定する

GoogleアナリティクスAPIのアカウントを取得したら、次に_config.ymlへ追加します。

_config.yml
1
# hexo-related-popular-posts 人気記事を使用するためのオプション指定
# Google Anakytics APIのアカウント情報を追記します
popularPosts:
  googleAnalyticsAPI:
    clientId: ******.apps.googleusercontent.com           # google analytics APIで取得したサービスアカウント
    serviceEmail: *****@developer.gserviceaccount.com     # google analytics APIで取得したメールアドレス
    key: /hexo-project-root/path/to/google-services.pem   # google analytics APIで取得したpemファイルのパス
    viewId: 12345678                                      # google analyticsで紐付けたビューID
    dateRange: 30                                         # (オプション) PV数を集計したい期間(日数) デフォルトは30
    expiresDate: 10                                       # (オプション) キャッシュファイルの有効期限(日数) デフォルトは10

環境変数からアカウントを参照する

_config.ymlにGoogle Analytics APIのアカウント情報を書きたくない場合には、環境変数から参照することもできます。予め環境変数にアカウントを保存してください。

bash
1
2
3
4
$ export GOOGLEAPI_CLIENTID="******.apps.googleusercontent.com"
$ export GOOGLEAPI_EMAIL="*****@developer.gserviceaccount.com"
$ export GOOGLEAPI_KEY="/path/to/google-services.pem"
$ export GOOGLEAPI_ANALYTICS_TABLE="ga:12345678"

環境変数の保存が完了したら、_config.ymlに記述したGoogle Analytics APIのアカウントに関するオプション値を削除してください。プラグインは_config.ymlにGoogle Analytics APIのアカウント情報が存在しない場合、次に環境変数を参照する設計となっています。

_config.yml
1
popularPosts:
  googleAnalyticsAPI:
    # clientId: ******.apps.googleusercontent.com           # (削除) google analytics APIで取得したサービスアカウント
    # serviceEmail: *****@developer.gserviceaccount.com     # (削除) google analytics APIで取得したメールアドレス
    # key: /hexo-project-root/path/to/google-services.pem   # (削除) google analytics APIで取得したpemファイルのパス
    # viewId: 12345678                                      # (削除) google analyticsで紐付けたビューID
    dateRange: 60      # PV数を集計する期間(日数)
    expiresDate: 10    # キャッシュファイルの有効期限(日数)

記事本文と関連する記事

プラグインの通常の動作では、記事のタグと共通する記事を関連記事として選び出してくれます。更に記事本文と関連した記事を選び出すには、morphologicalAnalysisオプションを追記してください。

最も簡単に導入したい場合は、以下の様にオプションを記述します。

_config.yml
1
popularPosts:
  morphologicalAnalysis:

また、詳細なオプションは以下のような内容が指定できます。

_config.yml
1
popularPosts:
  morphologicalAnalysis: 
    negativeKeywordsList: hexo-rpp-negativewords.txt  # (オプション) 記事のキーワードとして除外したい文字が保存された除外リスト。正規表現の改行区切りデータが使える(Hexoプロジェクトからの相対パス)
    limit: 300              # (オプション) 一つの記事につきキャッシュするキーワードの上限数
  weight:                   # (オプション) 表示順位の重み設定
    tagRelevancy: 1.0       # (オプション) 関連記事を選び出す際に判断される、タグの関連度の重み
    contentsRelevancy: 1.0  # (オプション) 関連記事を選び出す際に判断される、記事本文の関連度の重み

negativeKeywordsListオプション

negativeKeywordsListオプションは、キーワード除外リストです。記事本文の関連を調べる為に、プラグインが記事本文中に含まれる名詞をキーワードとして抽出します。そしてプラグインがキーワードが多く含まれる記事を、関連記事として選び出します。

ただし、プラグインが抽出してしまう名詞には、除外したい文字も出てくるかと思います。そこで、除外しておきたい文字があれば、negativeKeywordsListオプションで指定したテキストファイルに指定して下さい。

例えば除外キーワードの指定例は以下のようになります。

hexo-rpp-negativewords.txt
1
関連キーワードから除外しておきたい文字列を正規表現で指定する。この行で指定した文字は部分一致したら除外扱いとなる
^関連キーワードから除外しておきたい文字列を正規表現で指定する。この行で指定した文字は完全一致したら除外扱いとなる$
^.$
^[0-9]+$

上記のように、hexo-rpp-negativewords.txtには正規表現の改行で区切ったデータを保存して使います。

キャッシュ機能

Hexoでは編集していない古い投稿記事であっても、全て最初から全て解析を行ってしまいます。(※Hexo3.2以降ではキャッシュ機能が実装されました)

そこで当プラグインでは、解析時間を短くするために、編集していない過去の記事についてはキャッシュデータを使うことで、プラグイン動作を高速にする事ができます。キャッシュ機能を有効にする場合にはcacheオプションを追記して下さい。

1
popularPosts:
  cache:                                                # (オプション) キャッシュの使用。デフォルトは非使用
    path: hexo-popular-related-posts-cached.json        # (オプション) キャッシュファイルの保存先(相対パス)

上記で指定した相対パスに、キャッシュファイルが作成されます。_config.yml、またnegativeKeywordsListオプションで指定された除外リストや投稿記事を編集していない時は、再び解析されることがありません。

ログの表示

プラグインにより、キャッシュファイルの作成や、Googleアナリティクスのデータ取得、関連記事の頻出キーワード解析を行った結果が、ターミナルで表示されます。これを非表示にしたい場合にはlogオプションをfalseに指定します。

1
popularPosts:
  log: false  # (オプション) ログの表示。デフォルトはtrue(表示)

リストのHTMLをカスタマイズ

生成される関連記事や人気記事のHTMLを独自にカスタマイズしたい場合には、JSON出力できるヘルパータグpopular_posts_json()を使用します。

そして、popular_posts_json()タグから生成されるJSONを使って、HTMLに整形するためのヘルパータグhtmlGenerator()を新たに用意してください。

まず初めに、テンプレートへ以下のようにタグを記述していきます。

themes/your-theme/layout/path-to-your-article.ejs
1
<%-
    htmlGenerator( 
        popular_posts_json({ maxCount: 5 , ulClass: 'popular-posts' , PPMixingRate: 0.0 , isDate: true , isImage: true , isExcerpt: true})
    )
%>

続いて、以下ようにJSONをHTMLに整形するヘルパータグを記述します。

themes/your-theme/scripts/exampleHelper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Examples of helper 
hexo.extend.helper.register('htmlGenerator', function(args){
if(!args || !args.json || args.json.length == 0)return "";

var returnHTML = "";

function generateHTML(list){
var ret = "";
ret += "<li class=\"" + args.class + "-item\">";

if(list.date && list.date != ""){
ret += '<div class="'+args.class+'-date">' + list.date + "</div>";
}

if(list.img && list.img != ""){
ret += '<div class="'+args.class+'-img">' + '<img src="'+list.img+'" />' + "</div>";
}
ret += '<div class="'+args.class+'-title"><h3><a href="' + list.path + '" title="'+ list.title +'" rel="bookmark">'+ list.title + "</a></h3></div>";
if(list.excerpt && list.excerpt != ""){
ret += '<div class="'+args.class+'-excerpt"><p>' + list.excerpt + "</p></div>";
}

ret += "</li>";
return ret;
}

for(var i=0; i<args.json.length; i++){
returnHTML += generateHTML(args.json[i]);
}

if(returnHTML != "")returnHTML = "<ul class=\"" + args.class + "\">" + returnHTML + "</ul>";

return returnHTML;
});

これによりお好みのレイアウトで関連記事や人気記事を生成することができます。

訪問者数の表示

各投稿ページに訪問数(ページビュー数)を表示することができます。いわゆるアクセスカウンターのような機能です。訪問数を表示させるには、 pvMeasurementsStartDateオプションを指定します。尚、訪問数の表示にはGoogle Analytics APIアカウントの取得が必要になります。

まずは以下のように _config.ymlを編集します。pvMeasurementsStartDateオプションには、訪問数を計測したい開始日時を指定してください。

_config.yml
1
popularPosts:
  # (optional) Popular posts options
  googleAnalyticsAPI:
    clientId: ******.apps.googleusercontent.com
    serviceEmail: *****@developer.gserviceaccount.com
    key: /hexo-project-root/path/to/google-services.pem
    viewId: 12345678
    dateRange: 30
    expiresDate: 10
    pvMeasurementsStartDate: 2015/11/01  #累計の訪問者数を計測したい開始日時を指定する

次に、記事のテンプレートに popular_posts_pv()ヘルパーを追記します。

themes/your-theme/layout/path-to-your-article.ejs
1
この記事の訪問数(ページビュー数)は
<%- popular_posts_pv() %>
回です。

アクセス数をランキングで取得

こちらはオマケ機能です。投稿記事ごとのページビュー数をランキング形式でまとめて確認する事ができます。アクセス数を取得するには、 rankingSheetオプションを指定します。尚、取得にはGoogle Analytics APIアカウントの取得が必要になります。

具体的には以下のように _config.ymlを編集します。

_config.yml
1
popularPosts:
  # (optional) Popular posts options
  googleAnalyticsAPI:
    clientId: ******.apps.googleusercontent.com
    serviceEmail: *****@developer.gserviceaccount.com
    key: /hexo-project-root/path/to/google-services.pem
    viewId: 12345678
    dateRange: 30
    expiresDate: 10
    pvMeasurementsStartDate: 2015/11/01  #累計の訪問者数を計測したい開始日時を指定する
    rankingSheet: rankingSheet.txt       #アクセス数の取得結果を保存するファイル名をここで指定する(Hexoプロジェクトからの相対パス)

利用者の声

中国や台湾情報交換が盛んなHexoですが、日本でも使ってくれる方の声が聞けるようになってきました。ご紹介いただきまして本当にありがとうございました。

YoshinoriN in HEXO
ブログ(Hexo)をカスタマイズしました : タグクラウドプラグイン & 関連記事表示プラグインの導入 & 一部シェアボタン追加

@Yoshinorinさんにはプラグインの挙動について怪しい部分もプルリクを頂きました^^

可愛いを叫ぶブログ
HEXOをカスタマイズしました(関連記事プラグイン導入とか)

キャッシュによるパフォーマンスの向上や、除外キーワードを使ったご感想をブログでご紹介いただきました。ブログ記事に頻繁に使われているキーワードは除外キーワードに指定しておく事で、関連記事抽出の判断材料から外れます(^ ^)

…そう自分も、「カメラ」とか「写真」とか「撮影」ばかりが抽出されて、関連も何も無いなと思い追加した機能でした。実際に使ったご感想を頂けて嬉しいです。

Azriton’s blog - 験なきものを思はずは
Hexo に 関連する記事のリストを追加する

プラグインの導入手順をレビュー&解説いただきました。人気記事と関連記事を混ぜ込む機能を気に言って頂き光栄です^^

Qiita
【随時更新】Hexo まとめ(Node.js製のブログ型静的サイトジェネレーター)

QiitaのHexoの導入方法に関する話題の中で、プラグインをご紹介いただきました。

海外でも、中国や韓国を中心に導入頂いてるようです。

MSFL::모리스 소프트웨어 공작소, IT Blog
Plugins - hexo-related-popular-posts 설정하기

上記の記事では、韓国語でプラグインの紹介をしていただきました。分かりやすい文章で書かれているようで、google翻訳で読み取ることができました。プラグインは細かい仕様となっていますが、詳細に翻訳して頂き感謝です。

最後に

今回はHexoから関連記事や人気記事を生成するプラグイン「hexo-related-popular-posts」についてご紹介しました。もし興味のある方がいらっしゃいましたらぜひ使ってみてください。

静的サイトジェネレータのような新たな選択肢「not CMS」の登場により、ブログ運用環境も変わってきそうですね。既に静的サイトジェネレータを導入されているHugo、Jekyll、middleman、Octopress、gatsbyユーザーの方にもプラグイン開発の参考になりましたら幸いです。それでは!

ご注意

サーバー上のデータベースやスクリプトからPV数を保持できるWordpressとは違い、Hexoでは生成された静的ファイルの配信しかできません。そこで当プラグインではローカルサーバーからgoogleアナリティクスのPV数を取得し、集計結果を生成しています。

従いまして、リアルタイムに関連記事を選び、表示させているわけではありませんので予めご了承ください。