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のような中小規模向けのアプリケーションがどんどん出てくると思う。