Vue.js初心者がWP REST APIとGA Reporting APIを使ってスワブロランキングアプリを作ったよ
2020.04.01

ども、むったんです。
コロナ騒ぎで、ライブ&イベントが軒並み中止になってとても辛い。
えぇ、分かってます。
ウイルスを蔓延させないためって分かってます。
分かってる…けどさぁーーー!推しに会いたいよーーー😭
ちなみに3月に開催予定だったライヴは中止となり、払い戻しが始まりました😭
ライブが開催できていれば、リアル推し×画面の中にいた2D推しが3Dになってステージで歌って、踊っていたと思うと…是非もう一度企画していただきたいと思う所存!
むったんが行きたかったライブはこちら
そして、そんな悶々とした気持ちをぶつけながら作ったのが、今回のアプリですww
スワブロランキングアプリを作った
作り始めた経緯
みなさんが見てくれているこのスワローブログ(略してスワブロ)ですが、運用が始まったのは2018年10月。
そこから月ごと&四半期ごとに、中村さんがGoogleアナリティクスからフィルターかけたりしてランキングを発表してくれていました。
正直この作業簡略化できそうだし、アプリにできたら中村さんの仕事の負担ちょっとは減るよなー…と思っていました。
なので、リーダー千に「中村さんの仕事の負担を減らすために、WPとアナリティクスからデータ引っ張ってきてスワブロランキングアプリ作ってええ?( ´ ▽ ` )ノ」とノリで言ったらOKをもらいました!
ということで、ざっくり書いていくよ😆
環境
- WordPress 5.3.2
- GoogleアナリティクスReporting API v4
- Vue.js 2.6.10
- vuex 3.1.0
- vuetify 2.1.0
取得したいもの
GoogleアナリティクスReporting API v4
- URL
- PV数
WP REST API
- URL
- 投稿日
- ブログタイトル
- 投稿者
投稿者情報が不要であれば、GoogleアナリティクスReporting APIのみで行けたのですが…そりゃそうね、複数人で運用しているブログだもの。
投稿者は知りたいよね。
インストールしたもの
- vuetify
詳しい導入方法は以前むつたくが紹介しているので、そちらをご覧ください。
やっぱりフレームワーク使うとラクだわ(諦めれば、cssとの戦いも放棄できると最近踏ん切りがついたw
Vue-CLI3から始めるUIフレームワーク 〜Vuetify〜
- moment.js
インストール方法は以下の記事が分かりやすいです。
Vue.jsで日付処理ライブラリMoment.jsを使う|WEB PIXEL
- vue-monthly-picker
年月だけ指定したかったので、以下のものを使用させていただきました🙏
- vue-google-api
Google ApiとGoogle認証を使用して、クライアント側の操作を行うのに必要なもの。
ざっくり流れはこんな感じ

WP REST APIは投稿順で情報をくれるっぽい。
GA Reporting APIは好きなようにソートできるっぽいので、PV昇順に指定。
for文で回して、GA Reporting APIから来た内容を基準にWP REST APIの情報もまとめて、ランキングを表示しました。
準備(WordPress)
WordPress4.7以上であれば、プラグインなどを使用せずに下記URLから記事情報等がjson形式で取得できます。
[cc_html5] http://[ドメイン]/wp-json/wp/v2/[slug] [/cc_html5]
ちなみに、今回はスワブロの情報を取得したいので、こんな感じになります。
[cc_html5] https://swallow-incubate.com/wp-json/wp/v2/blog [/cc_html5]
取得件数の初期値は10件です。
準備(Google API)
設定が面倒だったのはこっちだった…orz
そして未だちゃんと理解してないけど、動いてるから大丈夫なのかな😅
ざっくり説明すると、こんな感じ。
- Google API ConsoleからGoogle Analytics Reporting APIを選択
- プロジェクトを作成
- 認証情報を作成
- Google Analyticsにサービスアカウントを追加
- APIキーを発行
- OAuth2.0クライアントIDを発行(むったんはhttp://localhost:8080を許可、状況に応じて変更してください)
詳しいやり方はSAKI Web Design様のブログに書いてありますので、参考にしてみてください🙏
Google Analytics Reporting API V4 を使う①|SAKI Web Design
コード
※コードが長くなるので、CSSは書きません。
src/main.js
[cc_javascript]
import Vue from ‘vue’
import App from ‘./App.vue’
import router from ‘./router’
import store from ‘./store/index’
import vuetify from ‘./plugins/vuetify’
import VueGoogleApi from ‘vue-google-api’
const config = {
clientId: ‘[clientId]’,
scope: ‘https://www.googleapis.com/auth/analytics’,
apiKey: ‘[apiKey]’,
discoveryDocs: [‘https://analyticsreporting.googleapis.com/$discovery/rest?version=v4’]
}
Vue.config.productionTip = false
Vue.use(VueGoogleApi, config)
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount(‘#app’)
[/cc_javascript]
src/App.vue
[cc_javascript]
[/cc_javascript]
src/components/BaseLine.vue
[cc_javascript height=”600″]
[/cc_javascript]
src/views/Home.vue
[cc_javascript height=”600″]
{{displayUrl[index].pv}} pv
{{displayUrl[index].date}}
{{displayUrl[index].title}}
{{displayUrl[index].writer}}
[/cc_javascript]
src/store/index.js
[cc_javascript]
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: () => import('../views/Home.vue')
},
{
path: '/three-months',
name: 'threeMonths',
component: () => import('../views/ThreeMonths.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
[/cc_javascript]
src/store/modules/blog.js
[cc_javascript height="600"]
import axios from 'axios'
export const blog = {
namespaced: true,
state: {
url: 'https://swallow-incubate.com/wp-json/wp/v2/blog?'
},
actions: {
async getBlog (state, obj) {
const ApiURL = state.state.url + 'after=' + obj.params.after + '&before=' + obj.params.before + '&per_page=' + obj.params.perPage
const result = await axios.get(ApiURL)
.then(res => {
if (res.status === 200) {
return {
result: true,
data: res.data
}
} else {
return {
result: false,
code: res.code,
data: []
}
}
})
.catch(error => {
if(error.message) {
if (Array.isArray(error.message)) {
let message = []
for (let err of error.message) {
message.push(err.msg)
}
return {
result: false,
data: message.join(',')
}
} else {
return {
result: false,
data: error.message
}
}
} else {
return {
result: false,
data: error
}
}
})
return result
}
}
}
[/cc_javascript]
src/store/modules/gAnalytics.js
[cc_javascript height="600"]
export const gAnalytics = {
namespaced: true,
state: {
url:'https://analyticsreporting.googleapis.com/v4/reports:batchGet',
viewId: '[viewId]'
},
actions: {
async getGAnalytics ({state}, obj) {
const requestParams = {
reportRequests: [
{
viewId: state.viewId,
dateRanges: [
{
startDate: obj.params.start,
endDate: obj.params.end,
}
],
metrics: [
{
expression: 'ga:pageviews'
}
],
dimensions: [
{
name: 'ga:pageTitle'
},
{
name: 'ga:pagePath'
}
],
dimensionFilterClauses: [{
filters: [{
dimensionName: 'ga:pagePath',
expressions: [
obj.params.selectedMonth
]
},
{
dimensionName: 'ga:pagePath',
expressions: [
obj.params.conditions
]
}
]
}],
orderBys: [{
fieldName: "ga:pageviews",
sortOrder: "DESCENDING"
}]
}
]
}
const result = await obj.gapi.request({
path: state.url,
method: 'POST',
body: requestParams,
headers: {
'content-Type': 'application/json'
}
})
.then(res => {
if (res.status === 200) {
return {
result: true,
data: res.result.reports[0].data.rows
}
} else {
return {
result: false,
code: res.code,
data: []
}
}
})
.catch(err => {
console.log('gAnalytics', err)
})
return result
}
}
}
[/cc_javascript]
完成イメージ
cssもちゃんと設定してあげて、こんな感じになりました。

これは、2月1日〜2月29日までに公開されたブログで、かつ2月1日〜2月29日までのPV数をカウントした1ヶ月のランキング画面になっております!
1ヶ月単位で見ると、そんな大した数字ではないのですが...3ヶ月ごとになると、トップ5くらいが爆発的な数字になってるんですよね。
1位は大体あの方がさらっと取っていくのですが、2&3位争いはいつも熾烈を極めているスワブロです。
コードの説明
さてさて、垂れ流しで紹介したコードですが...ざっくり説明しますね!
src/main.js
インストールしたvue google apiをimportして、作成したGoogle APIの情報を入れます。
clientId = OAuth2.0クライアントIDから作成したクライアントID
apiKey = APIキーから作成したキー
src/App.vue
外枠を作ってあげてます。
コンポーネンツにBaseLine.vueを作っているので、そちらの読み込みをしています。
src/components/BaseLine.vue
vuetifyのナビゲーションドロワーを使っています。
ここでは、1ヶ月と3ヶ月のランキングへのメニューを作っていますが、今回は1ヶ月ランキングのみ紹介します。
src/views/Home.vue
html部分ではtop3をvuetifyのcardでレイアウトしています。
テーブルはvuetifyのData tableを使用しています(手動ソートにも対応しているため、超便利!)
script部分は細かいものはコメントで説明しましたが、ざっくり説明するとこんな感じ。
- セレクトボックスに今月を代入
- WPからデータを取得
- GA Reporting APIからデータを取得
- 上記で集計して、ソートがおかしければ再集計
ちなみに、セレクトボックスは年月のみの選択となっていますが、script内ではこんな感じになっています。
〜今月(今日が2020年4月03日11時00分00秒の場合)〜
集計開始日:2020-04-01
集計終了日:2020-04-03
集計開始日(秒まで):2020-04-01T00:00:00
集計終了日(秒まで):2020-04-03T11:00:00
〜今月以外(2月を選択した場合)〜
集計開始日:2020-02-01
集計終了日:2020-02-29
集計開始日(秒まで):2020-02-01T00:00:00
集計終了日(秒まで):2020-02-29T23:59:59
今月以外だったら1ヶ月分をごっそり集計しちゃえばいいのですが、今月の場合は今日のなうタイムまでのデータしか作成されていないので...今月以外と同じ処理でデータをgetすると「んなデータねぇーよ!この野郎!」と怒られますww
そのためにwatchのif部分は細かく設定してあげてます。
あと268行でPVでソートし直しとありますが...
ここは、恐らくやらなくても大丈夫なんです。
ただ、弊社の場合ちょっと前にサーバー移管とかなんやらやって、数ページだけGAで取得している情報が同じURLなのに別れてる...という珍事象がありまして(原因がこれかは分かりませんが...
GA上では合体しているんですが、APIから取得してくると別れてるんです。
archives/blog/xxxxxxxx/:500pv
archives/blog/xxxxxxxx/:2pv
みたいな?
中村さんから月間発表されて「たくやが1位になったのに、こっちのアプリではたくや3位なんですけどwwwあれ?分裂してるwww」っていうね。
src/store/index.js
コードがゴチャゴチャしてくるため、modules/blog.js, modules/gAnalytics.jsというモジュールを作って管理しています。
src/store/modules/blog.js
Home.vueでまとめたparams(after, before, perPage)を受け取り、それをWP Rest APIに投げて、statusが200だったらdataに格納して、Home.vueに値をぶん投げてます。
参照:WP REST API
src/store/modules/gAnalytics.js
Home.vueでまとめたparams(start, end, selectedMonth, conditions)を受け取り、reportRequestsに条件を当てはめていきGA Reporting APIに投げて、statusが200だったらdataに格納して、Home.vueに値をぶん投げてます。
まとめ
いかがでしたでしょうか?
超ローングな内容だったので、大分はしょっちゃいました😋
一応私のローカルでは動いてるんですが...だ、大丈夫かな(ひやひや
次の問題は、社内サーバーにあげようとしたらGoogle APIが「IPアドレスじゃ受け付けませんぜ!」と弾くんで、そこで頭を抱えています。
中村さんに使ってもらえなかったら、作った意味がーーー😭
ちなみに、このアプリはGAで見る権限がないと見れないのも注意!
(多分誰でも見られる方法もある気はするが...とりあえず、社内で見られればでこんな感じにしています)
無事、スワローメンバーに見てもらえる日が来ることを祈って...このブログを締めたいと思います!
ではっ!
↓↓↓ぜひチェックしてください
~提供中のヒューマンセンシング技術~
◆人物検出技術
歩行者・来店者数計測やロボット搭載も
https://humandetect.pas-ta.io
◆視線検出技術
アイトラッキングや次世代UIに
https://eyetrack.pas-ta.io
◆生体判定技術
eKYC・顔認証のなりすまし対策を!
https://bio-check.pas-ta.io
◆目検出技術
あらゆる目周りデータを高精度に取得
https://pupil.pas-ta.io
◆音声感情認識技術
会話から怒りや喜びの感情を判定
https://feeling.pas-ta.io
◆虹彩認証技術
目の虹彩を利用した生体認証技術
https://iris.pas-ta.io
