jsタグを貼るだけで無料で高度なアクセス解析ができるGoogle Analyticsはもはやデファクトスタンダード。
でも、個人を特定できるような生ログは無料版では手に入れられず、自社の会員IDと結びつけたり、特定の人の行動を追ったりすることは出来ない。
そこで、有料版のGoogle Analyticsプレミアム(Google Analytics 360?)なら、生ログをBigQueryにエクスポートできるので色々できるんだけれど、いかんせん値段が高すぎる。
ちょっと古い2012年の記事(1ドル80円計算が懐かしい)だと月100万
GoogleアナリティクスPremiumとは? 有料版GAの特徴・機能・対象を解説! | GoogleアナリティクスPremium【短期集中連載】 | Web担当者Forum
公式を見ると現在は段階的な価格設定になっているらしい。
www.google.com
でも、代理店が必須だし、そこの取り分などを考えると最低でも数十万単位はかかりそう。
ちなみに、GAプレミアムをBigQueryにエクスポートするとどんなログが取れるかというとサンプルのデータセットが公開されており、見ることができる。
データセット名は下記。
google.com:analytics-bigquery
テキストで書き出してみると、下記のようなRECORD型のREPEATED(入れ子)を複数持った、とても複雑なスキーマ定義がされているのが分かる。
※サンプルデータは2013年となっているので、もしかしたら現在は違っているかもしれない。
visitorId INTEGER NULLABLE
visitNumber INTEGER NULLABLE
visitId INTEGER NULLABLE
visitStartTime INTEGER NULLABLE
date STRING NULLABLE
totals RECORD NULLABLE
totals.visits INTEGER NULLABLE
totals.hits INTEGER NULLABLE
totals.pageviews INTEGER NULLABLE
totals.timeOnSite INTEGER NULLABLE
totals.bounces INTEGER NULLABLE
totals.transactions INTEGER NULLABLE
totals.transactionRevenue INTEGER NULLABLE
totals.newVisits INTEGER NULLABLE
trafficSource RECORD NULLABLE
trafficSource.referralPath STRING NULLABLE
trafficSource.campaign STRING NULLABLE
trafficSource.source STRING NULLABLE
trafficSource.medium STRING NULLABLE
trafficSource.keyword STRING NULLABLE
trafficSource.adContent STRING NULLABLE
device RECORD NULLABLE
device.browser STRING NULLABLE
device.browserVersion STRING NULLABLE
device.operatingSystem STRING NULLABLE
device.operatingSystemVersion STRING NULLABLE
device.isMobile BOOLEAN NULLABLE
device.flashVersion STRING NULLABLE
device.javaEnabled BOOLEAN NULLABLE
device.language STRING NULLABLE
device.screenColors STRING NULLABLE
device.screenResolution STRING NULLABLE
customDimensions RECORD REPEATED
customDimensions.index INTEGER NULLABLE
customDimensions.value STRING NULLABLE
hits RECORD REPEATED
hits.hitNumber INTEGER NULLABLE
hits.time INTEGER NULLABLE
hits.hour INTEGER NULLABLE
hits.minute INTEGER NULLABLE
hits.isSecure BOOLEAN NULLABLE
hits.isInteraction BOOLEAN NULLABLE
hits.referer STRING NULLABLE
hits.page RECORD NULLABLE
hits.page.pagePath STRING NULLABLE
hits.page.hostname STRING NULLABLE
hits.page.pageTitle STRING NULLABLE
hits.page.searchKeyword STRING NULLABLE
hits.page.searchCategory STRING NULLABLE
hits.transaction RECORD NULLABLE
hits.transaction.transactionId STRING NULLABLE
hits.transaction.transactionRevenue INTEGER NULLABLE
hits.transaction.transactionTax INTEGER NULLABLE
hits.transaction.transactionShipping INTEGER NULLABLE
hits.transaction.affiliation STRING NULLABLE
hits.transaction.currencyCode STRING NULLABLE
hits.transaction.localTransactionRevenue INTEGER NULLABLE
hits.transaction.localTransactionTax INTEGER NULLABLE
hits.transaction.localTransactionShipping INTEGER NULLABLE
hits.item RECORD NULLABLE
hits.item.transactionId STRING NULLABLE
hits.item.productName STRING NULLABLE
hits.item.productCategory STRING NULLABLE
hits.item.productSku STRING NULLABLE
hits.item.itemQuantity INTEGER NULLABLE
hits.item.itemRevenue INTEGER NULLABLE
hits.item.currencyCode STRING NULLABLE
hits.item.localItemRevenue INTEGER NULLABLE
hits.contentInfo RECORD NULLABLE
hits.contentInfo.contentDescription STRING NULLABLE
hits.appInfo RECORD NULLABLE
hits.appInfo.name STRING NULLABLE
hits.appInfo.version STRING NULLABLE
hits.appInfo.id STRING NULLABLE
hits.appInfo.installerId STRING NULLABLE
hits.exceptionInfo RECORD NULLABLE
hits.exceptionInfo.description STRING NULLABLE
hits.exceptionInfo.isFatal BOOLEAN NULLABLE
hits.eventInfo RECORD NULLABLE
hits.eventInfo.eventCategory STRING NULLABLE
hits.eventInfo.eventAction STRING NULLABLE
hits.eventInfo.eventLabel STRING NULLABLE
hits.eventInfo.eventValue INTEGER NULLABLE
hits.customVariables RECORD REPEATED
hits.customVariables.index INTEGER NULLABLE
hits.customVariables.customVarName STRING NULLABLE
hits.customVariables.customVarValue STRING NULLABLE
hits.customDimensions RECORD REPEATED
hits.customDimensions.index INTEGER NULLABLE
hits.customDimensions.value STRING NULLABLE
hits.customMetrics RECORD REPEATED
hits.customMetrics.index INTEGER NULLABLE
hits.customMetrics.value INTEGER NULLABLE
hits.type STRING NULLABLE
fullVisitorId STRING NULLABLEデータのプレビューは横に長過ぎて出せないので、JSON型で一つだけ出してみる。
[
{
"visitorId": null,
"visitNumber": "1",
"visitId": "1378805776",
"visitStartTime": "1378805776",
"date": "20130910",
"totals": {
"visits": "1",
"hits": "8",
"pageviews": "5",
"timeOnSite": "468",
"bounces": null,
"transactions": null,
"transactionRevenue": null,
"newVisits": "1"
},
"trafficSource": {
"referralPath": "/urbancycling/reviews/foldable-helmet-from-lch.html",
"campaign": null,
"source": "technologysauce.com",
"medium": "referral",
"keyword": null,
"adContent": null
},
"device": {
"browser": "Firefox",
"browserVersion": "23.0",
"operatingSystem": "Linux",
"operatingSystemVersion": "x86_64",
"isMobile": "false",
"flashVersion": "(not set)",
"javaEnabled": "false",
"language": "en-us",
"screenColors": "24-bit",
"screenResolution": "1920x1200"
},
"customDimensions": [],
"hits": [
{
"hitNumber": "1",
"time": "0",
"hour": "9",
"minute": "36",
"isSecure": null,
"isInteraction": null,
"referer": null,
"page": {
"pagePath": "/helmets/foldable.html",
"hostname": "londoncyclehelmet.com",
"pageTitle": "London Cycle Helmet - Helmets - Foldable",
"searchKeyword": null,
"searchCategory": null
},
"transaction": {
"transactionId": null,
"transactionRevenue": null,
"transactionTax": null,
"transactionShipping": null,
"affiliation": null,
"currencyCode": null,
"localTransactionRevenue": null,
"localTransactionTax": null,
"localTransactionShipping": null
},
"item": {
"transactionId": null,
"productName": null,
"productCategory": null,
"productSku": null,
"itemQuantity": null,
"itemRevenue": null,
"currencyCode": null,
"localItemRevenue": null
},
"contentInfo": {
"contentDescription": null
},
"appInfo": {
"name": null,
"version": null,
"id": null,
"installerId": null
},
"exceptionInfo": {
"description": null,
"isFatal": "false"
},
"eventInfo": {
"eventCategory": null,
"eventAction": null,
"eventLabel": null,
"eventValue": null
},
"customVariables": [],
"customDimensions": [
{
"index": "1",
"value": "Helmets"
}
],
"customMetrics": [],
"type": "PAGE"
},
{
"hitNumber": "2",
"time": "6998",
"hour": "9",
"minute": "36",
"isSecure": null,
"isInteraction": null,
"referer": null,
"page": {
"pagePath": "/helmets/foldable.html",
"hostname": "londoncyclehelmet.com",
"pageTitle": "London Cycle Helmet - Helmets - Foldable",
"searchKeyword": null,
"searchCategory": null
},
"transaction": {
"transactionId": null,
"transactionRevenue": null,
"transactionTax": null,
"transactionShipping": null,
"affiliation": null,
"currencyCode": null,
"localTransactionRevenue": null,
"localTransactionTax": null,
"localTransactionShipping": null
},
"item": {
"transactionId": null,
"productName": null,
"productCategory": null,
"productSku": null,
"itemQuantity": null,
"itemRevenue": null,
"currencyCode": null,
"localItemRevenue": null
},
"contentInfo": {
"contentDescription": null
},
"appInfo": {
"name": null,
"version": null,
"id": null,
"installerId": null
},
"exceptionInfo": {
"description": null,
"isFatal": "false"
},
"eventInfo": {
"eventCategory": "View",
"eventAction": "LargeImage",
"eventLabel": "Foldable Helmet",
"eventValue": null
},
"customVariables": [],
"customDimensions": [
{
"index": "1",
"value": "Helmets"
}
],
"customMetrics": [],
"type": "EVENT"
},
{
"hitNumber": "3",
"time": "11249",
"hour": "9",
"minute": "36",
"isSecure": null,
"isInteraction": null,
"referer": null,
"page": {
"pagePath": "/helmets/foldable.html",
"hostname": "londoncyclehelmet.com",
"pageTitle": "London Cycle Helmet - Helmets - Foldable",
"searchKeyword": null,
"searchCategory": null
},
"transaction": {
"transactionId": null,
"transactionRevenue": null,
"transactionTax": null,
"transactionShipping": null,
"affiliation": null,
"currencyCode": null,
"localTransactionRevenue": null,
"localTransactionTax": null,
"localTransactionShipping": null
},
"item": {
"transactionId": null,
"productName": null,
"productCategory": null,
"productSku": null,
"itemQuantity": null,
"itemRevenue": null,
"currencyCode": null,
"localItemRevenue": null
},
"contentInfo": {
"contentDescription": null
},
"appInfo": {
"name": null,
"version": null,
"id": null,
"installerId": null
},
"exceptionInfo": {
"description": null,
"isFatal": "false"
},
"eventInfo": {
"eventCategory": "Basket",
"eventAction": "Add",
"eventLabel": "Foldable Helmet",
"eventValue": null
},
"customVariables": [],
"customDimensions": [
{
"index": "1",
"value": "Helmets"
}
],
"customMetrics": [],
"type": "EVENT"
},
{
"hitNumber": "4",
"time": "17466",
"hour": "9",
"minute": "36",
"isSecure": null,
"isInteraction": null,
"referer": null,
"page": {
"pagePath": "/",
"hostname": "londoncyclehelmet.com",
"pageTitle": "London Cycle Helmet",
"searchKeyword": null,
"searchCategory": null
},
"transaction": {
"transactionId": null,
"transactionRevenue": null,
"transactionTax": null,
"transactionShipping": null,
"affiliation": null,
"currencyCode": null,
"localTransactionRevenue": null,
"localTransactionTax": null,
"localTransactionShipping": null
},
"item": {
"transactionId": null,
"productName": null,
"productCategory": null,
"productSku": null,
"itemQuantity": null,
"itemRevenue": null,
"currencyCode": null,
"localItemRevenue": null
},
"contentInfo": {
"contentDescription": null
},
"appInfo": {
"name": null,
"version": null,
"id": null,
"installerId": null
},
"exceptionInfo": {
"description": null,
"isFatal": "false"
},
"eventInfo": {
"eventCategory": null,
"eventAction": null,
"eventLabel": null,
"eventValue": null
},
"customVariables": [],
"customDimensions": [],
"customMetrics": [],
"type": "PAGE"
},
{
"hitNumber": "5",
"time": "20211",
"hour": "9",
"minute": "36",
"isSecure": null,
"isInteraction": null,
"referer": null,
"page": {
"pagePath": "/vests/",
"hostname": "londoncyclehelmet.com",
"pageTitle": "London Cycle Helmet - Helmets",
"searchKeyword": null,
"searchCategory": null
},
"transaction": {
"transactionId": null,
"transactionRevenue": null,
"transactionTax": null,
"transactionShipping": null,
"affiliation": null,
"currencyCode": null,
"localTransactionRevenue": null,
"localTransactionTax": null,
"localTransactionShipping": null
},
"item": {
"transactionId": null,
"productName": null,
"productCategory": null,
"productSku": null,
"itemQuantity": null,
"itemRevenue": null,
"currencyCode": null,
"localItemRevenue": null
},
"contentInfo": {
"contentDescription": null
},
"appInfo": {
"name": null,
"version": null,
"id": null,
"installerId": null
},
"exceptionInfo": {
"description": null,
"isFatal": "false"
},
"eventInfo": {
"eventCategory": null,
"eventAction": null,
"eventLabel": null,
"eventValue": null
},
"customVariables": [],
"customDimensions": [
{
"index": "1",
"value": "Vests"
}
],
"customMetrics": [],
"type": "PAGE"
},
{
"hitNumber": "6",
"time": "22695",
"hour": "9",
"minute": "36",
"isSecure": null,
"isInteraction": null,
"referer": null,
"page": {
"pagePath": "/vests/yellow.html",
"hostname": "londoncyclehelmet.com",
"pageTitle": "London Cycle Helmet - Vests - Yellow",
"searchKeyword": null,
"searchCategory": null
},
"transaction": {
"transactionId": null,
"transactionRevenue": null,
"transactionTax": null,
"transactionShipping": null,
"affiliation": null,
"currencyCode": null,
"localTransactionRevenue": null,
"localTransactionTax": null,
"localTransactionShipping": null
},
"item": {
"transactionId": null,
"productName": null,
"productCategory": null,
"productSku": null,
"itemQuantity": null,
"itemRevenue": null,
"currencyCode": null,
"localItemRevenue": null
},
"contentInfo": {
"contentDescription": null
},
"appInfo": {
"name": null,
"version": null,
"id": null,
"installerId": null
},
"exceptionInfo": {
"description": null,
"isFatal": "false"
},
"eventInfo": {
"eventCategory": null,
"eventAction": null,
"eventLabel": null,
"eventValue": null
},
"customVariables": [],
"customDimensions": [
{
"index": "1",
"value": "Vests"
}
],
"customMetrics": [],
"type": "PAGE"
},
{
"hitNumber": "7",
"time": "23938",
"hour": "9",
"minute": "36",
"isSecure": null,
"isInteraction": null,
"referer": null,
"page": {
"pagePath": "/vests/yellow.html",
"hostname": "londoncyclehelmet.com",
"pageTitle": "London Cycle Helmet - Vests - Yellow",
"searchKeyword": null,
"searchCategory": null
},
"transaction": {
"transactionId": null,
"transactionRevenue": null,
"transactionTax": null,
"transactionShipping": null,
"affiliation": null,
"currencyCode": null,
"localTransactionRevenue": null,
"localTransactionTax": null,
"localTransactionShipping": null
},
"item": {
"transactionId": null,
"productName": null,
"productCategory": null,
"productSku": null,
"itemQuantity": null,
"itemRevenue": null,
"currencyCode": null,
"localItemRevenue": null
},
"contentInfo": {
"contentDescription": null
},
"appInfo": {
"name": null,
"version": null,
"id": null,
"installerId": null
},
"exceptionInfo": {
"description": null,
"isFatal": "false"
},
"eventInfo": {
"eventCategory": "Basket",
"eventAction": "Add",
"eventLabel": "Yellow Vest",
"eventValue": null
},
"customVariables": [],
"customDimensions": [
{
"index": "1",
"value": "Vests"
}
],
"customMetrics": [],
"type": "EVENT"
},
{
"hitNumber": "8",
"time": "0",
"hour": "9",
"minute": "44",
"isSecure": null,
"isInteraction": null,
"referer": null,
"page": {
"pagePath": "/",
"hostname": "londoncyclehelmet.com",
"pageTitle": "London Cycle Helmet",
"searchKeyword": null,
"searchCategory": null
},
"transaction": {
"transactionId": null,
"transactionRevenue": null,
"transactionTax": null,
"transactionShipping": null,
"affiliation": null,
"currencyCode": null,
"localTransactionRevenue": null,
"localTransactionTax": null,
"localTransactionShipping": null
},
"item": {
"transactionId": null,
"productName": null,
"productCategory": null,
"productSku": null,
"itemQuantity": null,
"itemRevenue": null,
"currencyCode": null,
"localItemRevenue": null
},
"contentInfo": {
"contentDescription": null
},
"appInfo": {
"name": null,
"version": null,
"id": null,
"installerId": null
},
"exceptionInfo": {
"description": null,
"isFatal": "false"
},
"eventInfo": {
"eventCategory": null,
"eventAction": null,
"eventLabel": null,
"eventValue": null
},
"customVariables": [],
"customDimensions": [],
"customMetrics": [],
"type": "PAGE"
}
],
"fullVisitorId": "380066991751227408"
}
]こんな大きく複雑なデータを世界中のサイトのアクセスごとに保存しているGoogleのデータ力(でーたぢから)は莫大すぎて想像ができない。
で、月100万円もアクセス解析に使えないし、こんな複雑なデータもいらないけど、アクセスデータをBigQueryに入れたくて現在開発&運用中なのが、私が一人プロジェクトで作っているoceanus。
単純にGETやPOSTリクエストで受け取ったパラメーターをバリデーションして、BigQueryにストリーミングインサートで流し込むことができる。
リクエストは下記のような自作のjsビーコンや、フォーム、imgタグなどを利用しても送ることが出来るし、他のアプリからはhttpsでリクエストはすればいい。
https://www.bizocean.jp/oceanus/okeanides.js
WEBサーバー(apache、nginxとか)のログでもいいかもしれないが、サーバー側にfluentdを入れたりするのは大変だし、サイト上のjsから取れる情報の方が豊富なので、ビーコン型にしている。
一つのoceanusで複数の「サイト」という単位で、BigQueryのテーブルやスキーマを分けたり、RedisのPubSubや、Google Cloud PubSub経由でデータを受け取ることができる。
運用コストは月間1000万PV程度の自社のbizoceanで、ページビューやフォーム、クリックイベント等を取得しているが、Googleに支払うサーバー代などは全部合わせても2万円/月程度で賄うことができている。
今後、機械学習用にBigQueryのスキャン料金が増えてきても5万円程度だろうと推測している。
サーバーはDocker,KubernetesのGKEを利用しているのでデプロイや運用もかなり楽。稼働して10ヶ月なるが、ほぼデータ損失は起こっていない。
現在使っていBigQueryのスキーマは下記のようなもので、1行1イベントにしている。sessionはクッキーに入れて保存している。シンプルなので学習コストが低い。
dt STRING REQUIRED datetime with micro seconds oid STRING REQUIRED option id or oceanus id sid STRING REQUIRED session id uid STRING NULLABLE user id rad STRING REQUIRED remote address, ip evt STRING REQUIRED event name tit STRING NULLABLE title, page title etc url STRING NULLABLE url ref STRING NULLABLE referer jsn STRING NULLABLE json format text ua STRING NULLABLE user agent dev STRING NULLABLE device detected by ua enc STRING NULLABLE encode scr STRING NULLABLE screen size vie STRING NULLABLE view size
便利なのがjson型で、例えば検索時に見つからなかったキーワードを取りたいな、とかそういう時にぶち込むことができる。これのお陰でテーブルスキーマは複雑にならずに済む。
oceanusは一応オープンソースだけど、中身にはかなりbizocean用のものが多く入っており、そのまま使うことはできない。使ってみたいという人がいれば簡単な質問ベースであれば無料で出来る範囲で、自社用に立てて欲しい、カスタマイズしてほしいであればコンサルティング的に受けることができるので、自社サイトのデータをBigQueryに入れたいけどどうしたらいいか悩んでいる人はコメントください。
ビッグデータの収集や利用はGCPをはじめ、どんどん手軽になってきている。今後、oceanusのような中小規模向けのアプリケーションがどんどん出てくると思う。