Google Analyticsを使って記事ランキング機能を実装する

この記事は[東京理科大学 Advent Calendar 2017](https://qiita.com/advent-calendar/2018/tokyo_university_of_science) 12/28の記事です。嘘です。22日の記事です。
つまり遅刻しています。年を越さなくてよかったです。
# はじめに
自身のブログなどで記事人気ランキングを表示したい……。でも実装が面倒……。みたいなことはありませんか？  
今回はruby on rails上でGoogle Analyticsを利用してランキング機能を実装してみます。

注）実際に用いたコードから省略して記載しているので動作しない等あれば指摘お願いします。

## 環境
ruby 2.5.1
rails 5.2.1

## Google Analyticsを導入する
まずウェブアプリにGoogle Analyticsを導入しておいてください。Google Analyticsではpageview（ページアクセス数）とuniqpageview（ページにアクセスしたセッション数）を記録しているので、これを利用して記事ランキング機能を実装します。  
タグを埋め込んで導入する、gemによって導入するなどの方法がありますがどれでも構いません。導入方法は割愛します。

## APIの認証情報を用意する
現在(2018/12)の最新はv4なので、v4のapiを利用していきます。

[APIのページ](https://console.developers.google.com/projectselector/Google AnalyticsAPIs/dashboard)にアクセスしてプロジェクトを選択します。  
プロジェクトを作成していない場合は作成するよう言われるので作っておいてください。


[APIとサービスの有効化](https://console.developers.google.com/apis/library)よりanalyticsと検索して`Google Analytics Reporting API`を選択してください。`Reporting API`となっているものはv3のAPIなので利用しません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/145589/3aa4db57-9ad8-ce86-ede9-52dd67be9141.png)

有効にするを押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/145589/1665e2ae-c279-b786-227e-ca040d0a24bb.png)

「認証情報を作成」ボタンが出てくるのでこれを押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/145589/fd3d81f4-f9f9-5955-4477-36924af9483d.png)
認証情報の入力に移ります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/145589/acfc7abd-e2b3-1510-9028-40b3e57e17e3.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/145589/251dabf9-5a51-dc5d-a027-7d1847dc3402.png)

以上のように入力して認証情報を作成します。

作成すると認証用のjsonがダウンロードされるので紛失しないよう管理しておいてください。


さて、このままrailsから認証を行おうとしても
`Error - #<Google::Apis::ClientError: forbidden: User does not have any Google Analytics account.>`
となり怒られてしまいます。
これは、先程作成したアカウントとGoogle Analyticsのアカウントが紐ついてないためです。

[Google Analyticsのページ](https://analytics.google.com/analytics/web/)から左下から管理に移動してアカウント、プロパティ、ビューのいずれかのユーザー管理者の設定に移って新しくアカウントを登録します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/145589/98f31029-983a-6050-4877-51e13672b6bf.png)

先程ダウンロードしたjsonファイルの中身を見て`client_email`に記述されているメールアドレスを入力します。権限は表示と分析のみで構いません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/145589/696dacd1-e32d-1c0f-6022-4486cacd95cc.png)

以上で認証情報の用意は終了です。

## Google AnalyticsAPIを利用してgoogleanalyticsに接続する
googleではGoogle AnalyticsAPIの認証が共通なのでGemfileに以下のgemを追加します。

```
gem 'google-api-client'
```
ネットで情報を集める際はv3でなくv4のAPIで認証を行うことに注意してください。

認証を行うコードを実装します。
rails5ではapp以下が自動で読み込まれるのでapp/lib以下にファイルを追加するのが良いかと思います。

```
require 'google/apis/analyticsreporting_v4'

class Analytics
  def initialize(view_id)
    @view_id  = view_id
    scope = 'https://www.googleapis.com/auth/analytics.readonly'
    json_path = Rails.root.join("app", "lib", "analytics.json") //任意の場所にjsonファイルを配置

    @analytics = Google::Apis::AnalyticsreportingV4
    @client = @analytics::AnalyticsReportingService.new
    @client.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: File.open(json_path),
      scope: scope
    )
  end
end
```

先程のjsonファイルを任意の場所に配置し、読み込んで認証を行います。

## reportを作成してデータを取得する
参考：[Reporting API v4 デベロッパー ガイド](https://developers.google.com/analytics/devguides/reporting/core/v4/basics?hl=ja)

先程と同じファイルにレポートを作成する関数を実装します。

```
class Analytics
  def initialize(view_id)
    ...
  end

  def get_uniq_pageview(start_date='2005-01-01', end_date='today')
    date_range = @analytics::DateRange.new(start_date: start_date, end_date: end_date)
    metric     = @analytics::Metric.new(expression: 'ga:uniquePageviews', alias: 'uniquePageviews')
    dimension  = @analytics::Dimension.new(name: 'ga:pagePath')
    order_by   = @analytics::OrderBy.new(field_name: 'ga:uniquePageviews', sort_order: 'DESCENDING')
    dimension_filters = @analytics::DimensionFilterClause.new(
      filters: [
        @analytics::DimensionFilter.new(
          dimension_name: 'ga:pagePath',
          expressions: ['^/article/\w{20}']
        )
      ]
    )

    report_request = @analytics::GetReportsRequest.new(
      report_requests: [
        @analytics::ReportRequest.new(
          view_id: @view_id,
          metrics: [metric],
          date_ranges: [date_range],
          dimension_filter_clauses: [dimension_filters],
          dimensions: [dimension],
          order_bys: [order_by],
        )
      ]
    )

    response = @client.batch_get_reports(report_request)
    data = response.reports.first.data

    pageview = data.rows&.map{ |row| [ row.dimensions.first.slice(/\w+{20}/), row.metrics.first.values.first ] }.to_h
    return pageview
  end
end
```


`daterange`の日付はISO8601の日付形式です。またtoday、yesterdayなどの表現も利用できます。おそらく[adword Google AnalyticsAPI](https://developers.google.com/adwords/api/docs/guides/reporting?hl=ja#date_ranges)で利用できる表現と同様と思います。


また、`filter`で期待するデータを絞ることができます。
参考：[メソッド: reports.batchGet](https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet?hl=ja)  
今回は記事のページ以外のデータは必要ないので`DimensionFilterClause`で記事のurlを正規表現でフィルタしています。この場合は`/article/{20文字の英数字}`にマッチします。  

時間帯を更に細かく絞りたい場合はfilterで時間関連の指標を絞り込むといいと思います。
他にも色々と表現できるのでdocumentを参照してください。



APIのテストは [ここ](https://developers.google.com/apis-explorer/?hl=ja#p/analyticsreporting/v4/analyticsreporting.reports.batchGet) から簡単に試せるのでやってみてください。


また、デフォルトでは一度のリクエストで1000件までの取得なのでそれを超える場合はページの切り替え処理をする必要があります。

## 記事にview数を保存するタスクを作成する
アクセス毎にAPIを叩くわけには行かないのでタスク化して定期実行しDBにデータを持つようにします。  
まずview数を保存するためのカラムを追加します。

```
rails g migration add_day_pageview_to_article
```

migrationファイルは以下のようにしました。

```
class AddDayPageviewToArticle < ActiveRecord::Migration[5.2]
  def change
    add_column :articles, :day_pageview, :integer, null: false, default: 0
  end
end
```

これでテーブルに`day_pageview`カラムが追加されます。

更に、taskを作成します。

```
$ rails g task analytics
```

`lib/tasks/`以下にファイルが生成されるので編集していきます。
先程書いたクラスを使ってデータを読み込んで保存します。

```
namespace :analytics do
  desc 'import day uniq view'
  task :update_day_pageview_from_ga => :environment do

    view_id = ENV['GOOGLE_ANALYTICS_VIEW_ID']
    ga = Analytics.new(view_id)
    report = ga.get_uniq_pageview('yesterdat', 'yesterdat')

    pageview = Article.ids.map{|m| [m, 0]}.to_h
    pageview.merge(report)

    ActiveRecord::Base.transaction do
      pageview.each do |k, v|
        if article = Article.find(k)
          article.update!(day_pageview: v)
        end
      end
    end

    Rails.logger.info '閲覧数を更新しました'
  rescue => e
    Rails.logger.error '閲覧数更新に失敗しました'
    Rails.logger.error e.backtrace.join("\n")
  end
end
```
単純にデータを取ってきて保存するだけです。`ga.get_uniq_pageview('yesterdat', 'yesterdat')`の引数を変更すれば全期間や今月などの指定もできます。  
実際には保存の部分は[activerecord-import]()でbulk insertしたほうがいいかもしれません。

ここでの`view_id`はGoogle AnalyticsのビューIDです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/145589/0b79f802-9941-3df1-f073-fc7c9a1086f0.png)
先ほどの管理のページからビューの設定にいけば確認することができます。

## wheneverでタスクを定期実行する
wheneverというgemでcronを管理します。

```
gem 'whenever', require: false
```

初期設定を入れます。

```
wheneverize
```

詳しい使い方は割愛します。
`config/schedule.rb`に以下を追記します。

```
every 1.day, at: '0:10 am' do
  rake "rails analytics:update_day_pageview_from_ga"
end
```

これで毎日0:10分に前日のアクセス数が更新されます。  
以下のコマンドでバッチ処理させるサーバにcrontabを反映します。

```
bundle e whenever -i
```

crontabの内容は以下のコマンドで確認できます。

```
crontab -l
```

## 利用する
後は、

```
@articles = Article.all.order(day_pageview: :desc)
```

などのように利用できます。

## あとがき
Google Analyticsを利用することで簡単にアクセス数を取得できました。特にセッション数などは実装が面倒なのでそれがないだけで大分楽だと思います。

東京理科大学 Advent Calendarということで告知を。`コミックマーケット95 2日目(日) 12/30	タ36a` で東京理科大学葛飾電気工学同好会(EIC)の会誌を頒布します。電気街電子部品価格比較とか書いてます。興味あればぜひおこしください！