静的サイトジェネレータだとPVだとかをDBに保持出来ないので、 人気記事をランキングで表示したい場合に一手間必要になる。

Google Analyticsを導入している場合はAPIを叩くことでデータが取れるので、 Hugoの機能であるData Filesと合わせて使うことでなんとかなる。

Hugo - Data Files

Google AnalyticsのAPIを使う

GoogleのAPIはgmailのアカウントで認証を通せばアクセスすることが出来るが、 プログラムからアクセスしたい場合はサービスアカウントってのを用意することで認証を通すことができる。

Googleの仕様はかなり頻繁に変わるし、ここにあまり詳細なことを書く気になれないので以下のページを参考にしてもらいたい。

はじめてのアナリティクス API: サービス アカウント向け Python クイック スタート  |  アナリティクス Core Reporting API  |  Google Developers

このページにはAPIの有効化からSDKの導入、サンプルプログラムの実行までが丁寧に書かれている。 Googleのドキュメントはあまり読みやすいと思ったことはないが、 このページはかなり簡潔かつ丁寧に書かれているので詰まることなく目的を達成できると思う。

このURLはPython向けの説明だが、JavaとPHP、Javascriptについても専用のページが用意されている。 必要な方はそちらを参照するとよい。

PythonからGoogle Analytics APIにアクセスする

前述したURLに記載されているサンプルプログラムの中で、コアになるのはこの記述である。

  return service.data().ga().get(
      ids='ga:' + profile_id,
      start_date='7daysAgo',
      end_date='today',
      metrics='ga:sessions').execute()

これは期間だとかメトリクスだとかどういうデータを取るんだっていうのを定義している部分。 ここでURLのフィルタなども出来るので、自分のサイトのURL構造に応じて定義を作ればよい。

定義はQuery Explorerというテスト用の実行ツールを使って組み立てるとよい。

Query Explorer — Google Analytics Demos & Tools

ちなみに僕の運営する別サイトの場合はこういう定義にしている。

def get_results(service, profile_id):
    return service.data().ga().get(
            ids='ga:' + profile_id,
            start_date='1daysAgo',
            end_date='yesterday',
            metrics='ga:pageviews',
            dimensions='ga:pagePath',
            filters='ga:pagePath=~^/\d+\/',
            sort='-ga:pageviews').execute()

これは昨日から1日前までのPVをページパスごとに取得し、/\d+\/の正規表現に合致するURLのみでフィルタする、というもの。 つまり記事詳細のページだけに絞ってデータを取るだけのもの。

あとはPythonでJSONなりHugoのData Filesで取り扱える好きな形式にして吐き出せばよい。

Data Filesで読み込む

僕はJSONでSlugのみを吐き出すようにしているので、Hugoのテンプレートは次のようにしている。

<section>
  <header>よく読まれている記事</header>
  <div>
    <ul>
      {{ $popular_articles := where .Site.Pages "Slug" "in" .Site.Data.ga.popular_articles }}
      {{ range .Site.Data.ga.popular_articles }}
      {{ range where $popular_articles "Slug" . }}
      {{ .Render "li" }}
      {{ end }}
      {{ end }}
    </ul>
  </div>
</section>

HugoはSlugなどから一意のページをフィルタすることが出来ない(と思う)ので、 かなり苦しい感じの処理になっている。ここは各自好きな感じにしてもらえればいい。

生成速度を求めるのであればJSON上にページタイトルやサムネイルなど、表示したい情報を全部載せてしまうのがいいと思う。 そうした場合は.Renderも使えなくなるし記事の情報が多重管理になるので、そこだけ我慢する必要がある。

デプロイ

デプロイ前に$HUGO_ROOT/data/populars.jsonみたいな感じで吐き出しておくとかすればデプロイするたびに最新の情報で更新される。 こうすると更新がない期間は古い情報になってしまうので、cronだとかを使って定期的に更新するなどの工夫をする必要がある。

ちなみにこんな外部サービスもあるので、これらの作業がめんどうな人はこっちを使うといいかも。

Ranklet [ランクレット]