【Rails】Google Analytics APIを使って人気記事ランキングを作る
このブログの人気記事を取得する際にGoogle Analytics APIを使って頑張ったのでメモ。
Railsでアクセス数を元に人気ランキングを作る方法はいくつかあるけどこのブログはGoogle Analyticsを使っているのでどうせならAPIで取得しようと思い、実装しました。
前提
Google AnalyticsをRailsアプリに組み込んでいること。
Google Analyticsも導入はgemを使って簡単にできます。
bgarret/google-analytics-rails
google-analytics-rails - Rails 3 helpers to manage google analytics tracking. Mostly intended for small to medium websites.
ちなみにこのブログでは自分のアクセスをトラッキングしないようにadmin user(自分)のときはmetaタグを表示させてません。
application.html.erb
<%= analytics_init if Rails.env.production? && !admin_user? %>
Google Analytics APIの導入
こちらの記事が大変参考になります。
Google Analytics API を叩いてデータを取得するまでの流れ(Ruby) - QiitaQiita
業務でサイトに人気記事ランキングを実装することになって、そのデータを Google Analytics から取得することにしました。自サーバーにRedisなどを入れてPVデータをカウントしておく方法もありますが、Google Analyticsのデータを使えば任意のディメンションや指標を使った集計が簡単に行えるのが魅力です。
キーのタイプをJSONで取得していますが今回はP12を使用しました。
APIを使う
Google APIsの設定が済んだらコードを書いていきます。
まずはRakeタスクファイルを生成します。
lib/tasks/analytics.rake
とします。
こちらを参考にAPIを使います。
Google APIの認証
最初に、Google APIの認証をして利用できるようにします。
まずは以下のgemを入れます。
https://github.com/googleapis/google-api-ruby-client
gem 'google-api-client'
もろもろ設定書いていきます。
ここでは同ディレクトリにgoogle_analytics.p12
を置いてます。
issuerと認証キーのP12ファイルのパスワードは環境変数に入れてます。
以下コードで認証は終わりです。
# lib/tasks/analytics.rake
require 'google/apis/analyticsreporting_v4'
require 'google/api_client/auth/key_utils'
namespace :analytics do
desc 'get popular posts from google analytics'
task popular_posts: :environment do
keyfile = Rails.root.join('google_analytics.p12')
analytics = Google::Apis::AnalyticsreportingV4
client = analytics::AnalyticsReportingService.new
client.authorization = Signet::OAuth2::Client.new(
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
audience: 'https://accounts.google.com/o/oauth2/token',
scope: 'https://www.googleapis.com/auth/analytics.readonly',
issuer: ENV['GA_ISSUER'],
signing_key: Google::APIClient::KeyUtils.load_from_pkcs12(keyfile, ENV['GA_KEY_SECRET'])
)
client.authorization.fetch_access_token!
end
end
APIからデータ取得
APIの認証ができたので次に、欲しいデータを指定してリクエストを送ります。
人気記事ランキングを作りたいので、欲しいデータは「過去7日間のpageviewが多い順のpageのpath」です。
view_id = ENV['GA_VIEW_ID']
metric = analytics::Metric.new(expression: 'ga:pageviews', alias: 'pageviews')
dimension = analytics::Dimension.new(name: 'ga:pagePath')
date_range = analytics::DateRange.new(start_date: '7DaysAgo', end_date: 'today')
order_by = analytics::OrderBy.new(field_name: 'ga:pageviews', sort_order: 'DESCENDING')
request = analytics::GetReportsRequest.new(
report_requests: [analytics::ReportRequest.new(
view_id: view_id,
metrics: [metric],
dimensions: [dimension],
date_ranges: [date_range],
order_bys: [order_by]
)]
)
response = client.batch_get_reports(request)
解説していきます。
Google AnalyticsのView IDを定義します。
view_id = ENV['GA_VIEW_ID']
欲しいデータを指定します。
簡単に言うと、メトリックをpageviews、取得するものをpagePath、範囲を過去7日、ソートをpageviewsの多い順にします。
metric = analytics::Metric.new(expression: 'ga:pageviews', alias: 'pageviews')
dimension = analytics::Dimension.new(name: 'ga:pagePath')
date_range = analytics::DateRange.new(start_date: '7DaysAgo', end_date: 'today')
order_by = analytics::OrderBy.new(field_name: 'ga:pageviews', sort_order: 'DESCENDING')
以上で指定ができたので、リクエストを送り、レポートを取得します。
request = analytics::GetReportsRequest.new(
report_requests: [analytics::ReportRequest.new(
view_id: view_id,
metrics: [metric],
dimensions: [dimension],
date_ranges: [date_range],
order_bys: [order_by]
)]
)
response = client.batch_get_reports(request)
data = response.reports.first.data
変数dataとしていい感じに整形します。
dataの中身はこんな感じ。
=> #<Google::Apis::AnalyticsreportingV4::ReportData:0x00007ffab8642ef8
@maximums=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab8641fd0 @values=["67"]>],
@minimums=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab8640ef0 @values=["1"]>],
@row_count=31,
@rows=
[#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffabab77930
@dimensions=["/posts/rails-blog"],
@metrics=
[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffabab75ea0 @values=["67"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab864bf30
@dimensions=["/"],
@metrics=
[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab864b5d0 @values=["30"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab864acc0
@dimensions=["/posts/23-years-old-blog"],
@metrics=
[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab864a360 @values=["12"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab8649a50
@dimensions=["/posts/free-domain-and-https-heroku"],
@metrics=
[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab86490f0 @values=["11"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab86487e0
@dimensions=["/posts/make-markdown-editor"],
@metrics=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffabab6fb40 @values=["8"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffabab6c5d0
@dimensions=["/posts/axios-token-configure"],
@metrics=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab8653708 @values=["7"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab8652df8
@dimensions=["/about"],
@metrics=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab8652498 @values=["6"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab8651b88
@dimensions=["/categories/Life"],
@metrics=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab8651228 @values=["4"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab8650918
@dimensions=["/tags"],
@metrics=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffabab67f08 @values=["4"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffabab66568
@dimensions=["/categories/tech"],
@metrics=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab865ba48 @values=["3"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab865b138
@dimensions=["/posts/boiler-room-home-theater"],
@metrics=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab865a7d8 @values=["3"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab8659ec8
@dimensions=["/categories/Gadgets"],
@metrics=[#<Google::Apis::AnalyticsreportingV4::DateRangeValues:0x00007ffab8659568 @values=["2"]>]>,
#<Google::Apis::AnalyticsreportingV4::ReportRow:0x00007ffab8658c58
@dimensions=["/categories/Music"],
これでデータの取得はできました。
あとは人気記事を割り出すだけです。
記事を抽出
以下のように書きました。
num = 0
data.rows.each do |row|
next unless row.dimensions.to_s.include?('/posts/')
slug = row.dimensions.first.to_s[7..-1]
post = Post.find_by(slug: slug)
next if post.nil?
num += 1
popular_post = PopularPost.where(rank: num).first_or_create! do |new_popular_post|
new_popular_post.rank = num
new_popular_post.post = post
end
popular_post.update!(post_id: post.id)
puts "#{num}: #{row.metrics.first.values.first}views: #{post.title}"
break if num == 8
end
PopularPostモデルが出てきていますが、中身はこんな感じです。
#popular_post.rb
#t.integer "rank", null: false
#t.bigint "post_id"
#t.datetime "created_at", null: false
#t.datetime "updated_at", null: false
#t.index ["post_id"], name: "index_popular_posts_on_post_id"
class PopularPost < ApplicationRecord
belongs_to :post
end
簡単に解説すると、
dataのrowsを展開
data.rows.each do |row|
/posts/
をパスに含まないものはスキップ
next unless row.dimensions.to_s.include?('/posts/')
slugを抽出して記事をPostモデルから引っ張ってくる。
slug = row.dimensions.first.to_s[7..-1]
post = Post.find_by(slug: slug)
next if post.nil?
記事が入ったらnumを足してランクづけする。1〜8位まであるんですが、
例えば5位までしかDBになく、新しく6位があるときは作るようにfirst_or_create!
します。
これで更新された新しい順位が出力されます。
num += 1
popular_post = PopularPost.where(rank: num).first_or_create! do |new_popular_post|
new_popular_post.rank = num
new_popular_post.post = post
end
popular_post.update!(post_id: post.id)
puts "#{num}: #{row.metrics.first.values.first}views: #{post.title}"
break if num == 8
出力された値はこちら
1: 67views: Railsでブログを作ってみた
2: 12views: 23歳はブログを少しずつでも書こうと思う
3: 11views: 無料でドメイン割り当て、HTTPSを実現。Heroku + Cloudflare
4: 8views: 3分でマークダウンエディターを作る【UIkit】
5: 7views: axiosのヘッダーにtokenを埋め込んで常時使えるようにする
6: 3views: Boiler Roomをお酒片手にプロジェクターで観ると幸せになれる
7: 2views: 奇跡の自然、モハーの断崖。まさに世界の果て 【旅ログ】
まだ現時点では7記事しかないので7位までです。
こんなブログでも1週間で以外にもPV数ありました(ない)。
以上で人気記事ランキングの更新ができました。
完成コード
require 'google/apis/analyticsreporting_v4'
require 'google/api_client/auth/key_utils'
namespace :analytics do
desc 'get popular posts from google analytics'
task popular_posts: :environment do
keyfile = Rails.root.join('google_analytics.p12')
analytics = Google::Apis::AnalyticsreportingV4
client = analytics::AnalyticsReportingService.new
client.authorization = Signet::OAuth2::Client.new(
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
audience: 'https://accounts.google.com/o/oauth2/token',
scope: 'https://www.googleapis.com/auth/analytics.readonly',
issuer: ENV['GA_ISSUER'],
signing_key: Google::APIClient::KeyUtils.load_from_pkcs12(keyfile, ENV['GA_KEY_SECRET'])
)
client.authorization.fetch_access_token!
view_id = ENV['GA_VIEW_ID']
metric = analytics::Metric.new(expression: 'ga:pageviews', alias: 'pageviews')
dimension = analytics::Dimension.new(name: 'ga:pagePath')
date_range = analytics::DateRange.new(start_date: '7DaysAgo', end_date: 'today')
order_by = analytics::OrderBy.new(field_name: 'ga:pageviews', sort_order: 'DESCENDING')
request = analytics::GetReportsRequest.new(
report_requests: [analytics::ReportRequest.new(
view_id: view_id,
metrics: [metric],
dimensions: [dimension],
date_ranges: [date_range],
order_bys: [order_by]
)]
)
response = client.batch_get_reports(request)
data = response.reports.first.data
num = 0
data.rows.each do |row|
next unless row.dimensions.to_s.include?('/posts/')
slug = row.dimensions.first.to_s[7..-1]
post = Post.find_by(slug: slug)
next if post.nil?
num += 1
popular_post = PopularPost.where(rank: num).first_or_create! do |new_popular_post|
new_popular_post.rank = num
new_popular_post.post = post
end
popular_post.update!(post_id: post.id)
puts "#{num}: #{row.metrics.first.values.first}views: #{post.title}"
break if num == 8
end
end
end
実際に人気記事を取得する
Rakeタスクファイルに書いてあるので、ターミナルで
bundle exec rake analytics:popular_posts
と実行するだけです。
このブログはHerokuで動かしているので Heroku Schedulerを使って毎日0時に↑のコマンドを実行してます。
つまり、毎日、1週間の人気記事が更新されます。
以上、こんな感じで人気記事を取得しました。
人気記事取得のコードはもっとうまく書けるのかな。あったら教えてください。