GitHub Pagesで公開している複数サイトの日別PV/UUを、自分専用のダッシュボードで見るための 自作アクセス解析システムです。
構成: tracker.js(計測)→ collector.gs(受信)→ スプレッドシート(raw/daily)
→ dashboard.gs + dashboard.html(集計・表示)。collector と dashboard は
アクセスできるユーザーの設定が異なる(前者は全員、後者は自分のみ)ため、あえて別々の
GASプロジェクトに分けている。
https://docs.google.com/spreadsheets/d/【ここがID】/edit の
【ここがID】部分(英数字とハイフンの文字列)をコピーしてメモしておく。
これが以降 SPREADSHEET_ID として使う値です。script.google.com を開き、「新しいプロジェクト」をクリックする。コード.gs の中身を全て削除し、このリポジトリの collector.gs の内容を
貼り付ける。SPREADSHEET_ID の値を、手順1でメモしたスプレッドシートIDに書き換える。ALLOWED_SITE_IDS の配列に、計測したいサイトのsiteId(自分で決める識別子、例:
'portfolio', 'keiba-simulator')を追加する。「デプロイ」をクリックする。初回はGoogleアカウントでの確認画面が表示されるので、 自分のアカウントで許可する。
このとき、「このアプリは Google で確認されていません」(英語表示では “Google hasn’t verified this app”)という警告画面が出ることがある。これは 自分で作成した個人のスクリプト(Googleによる審査を受けていないアプリ)では 必ず表示されるもので、異常ではない。画面左下の「詳細」をクリックし、 「(プロジェクト名)(安全ではないページ)に移動」といったリンクをクリックすると、 通常のGoogleアカウント権限確認画面に進める。これは自分自身が作成したスクリプトが 自分のスプレッドシートにアクセスする許可を求めているだけなので、そのまま許可して 進めて問題ない。
https://script.google.com/macros/s/.../exec の形式)を
コピーしてメモしておく。これが tracker.js の送信先エンドポイントです。script.google.com を開き、「新しいプロジェクト」をクリックする(collectorとは別の
新規プロジェクトにすること)。コード.gs の中身を全て削除し、このリポジトリの dashboard.gs の内容を貼り付ける。SPREADSHEET_ID の値を、手順1でメモしたスプレッドシートIDに書き換える
(collector.gsと同じ値)。dashboard
(拡張子は自動で.htmlになる)にする。dashboard.html の中身を全て削除し、このリポジトリの dashboard.html の
内容を貼り付ける。setup を選び、
「実行」ボタン(▷)をクリックする。初回は権限の許可を求められるので、自分のGoogle
アカウントで許可する(初回のみ、ここでも手順2-11と同じ「Google で確認されていません」
という確認画面が出ます。上記の手順と同様に、詳細→安全ではないページに移動、と進んで
許可してください)。raw と daily の2つのシートが作られ、ヘッダー行が入って
いることを確認する。なお setup はdate列(A列)をプレインテキスト書式にも自動設定する
ため(日付文字列がGoogleスプレッドシートによって日付型に勝手に変換されるのを防ぐ目的)、
手動で書式を設定する必要はありません。createDailyTrigger に変え、実行する。これで毎日決まった時刻に
aggregateDaily が自動実行されるようになる(トリガーは「トリガー」画面
(時計アイコン)から確認できる)。デプロイ後に dashboard.gs や dashboard.html を編集した場合も、変更をダッシュボードURLに
反映するには再デプロイ(「デプロイ」→「デプロイを管理」→ 既存デプロイの編集アイコン→
バージョン「新バージョン」→デプロイ)が必要なので忘れないこと。
main / フォルダ: /(root))。tracker.js の1行目付近の ENDPOINT の値を、手順2-12でメモした collector の
ウェブアプリURLに書き換えてコミット・pushする。https://<あなたのGitHubユーザー名>.github.io/site-analytics/tracker.js?v=1
でファイルが表示されることを確認する。各サイトのHTMLの </body> 直前などに以下を1行追加する(data-site-id は
collector.gs の ALLOWED_SITE_IDS に登録した値と一致させる):
<script src="https://<あなたのGitHubユーザー名>.github.io/site-analytics/tracker.js?v=1" data-site-id="portfolio"></script>
新しいサイトを追加するときは、collector.gs の ALLOWED_SITE_IDS にそのsiteIdを追加して
再デプロイ(「デプロイ」→「デプロイを管理」→ 既存デプロイの編集アイコン→
バージョン「新バージョン」→デプロイ)することを忘れないこと。
tracker.js の中身を変更した場合、ブラウザやCDNにキャッシュされた古い版が使われ続けることが
あるため、以下の手順でキャッシュを回避する。
tracker.js を修正してGitHubにpushする。tracker.js?v=1 → tracker.js?v=2)。以下の手順でエラーが出た場合は、まず collector.gs と dashboard.gs の両方で
SPREADSHEET_ID を実際のスプレッドシートIDに書き換えているか確認してください。
.../exec へのPOSTリクエストが飛んでいることを確認する。raw シートに1行追記されていることを確認する。aggregateDaily を手動実行し、daily シートに
前日分の集計行ができていること、90日より前の raw 行が削除されていることを確認する。getTodayStats は引数 siteId を取る関数のため、関数選択のドロップダウンから
直接実行することはできない。確認する場合は、dashboard プロジェクトのGASエディタに
function testGetTodayStats() { Logger.log(getTodayStats('portfolio')); } のような
一時的な関数を追加してこちらを実行する。実行後に自動で開く「実行ログ」欄に
{pv: ..., uu: ...} の形で当日のPV/UUが表示されることを確認する。確認できたら
このテスト用関数は削除してよい。collector.gs は「アクセスできるユーザー: 全員」で
デプロイするため、siteId さえ一致すれば第三者が任意のデータを送り込める
(例: PV/UUの水増し)。個人の参考指標としての利用を想定しており、署名検証などの
厳密な不正対策は行っていない。collector.gs の sanitizeForSheet_() により date/timestamp/
path/referrer/ua の各値が =/+/-/@ で始まる場合は先頭にシングルクォートを
付与してから書き込んでいる。これにより、スプレッドシート上でその値が数式として実行され
他のセルの参照・外部送信などに悪用されることは防いでいる。あくまで「書き込まれた値が
数式として実行されない」ようにする対策であり、「偽のデータが書き込まれること」自体を
防ぐものではない点に注意(この2つは別のリスク)。getTodayStats() のパフォーマンス: ダッシュボードの当日分表示は raw シートを
毎回全走査して集計するため、raw の行数が増えるほど表示が遅くなる。
aggregateDaily() による90日超rawの自動削除で行数の上限は一定に保たれるが、
直近90日間のアクセスが非常に多い場合は体感速度に影響し得る。raw/dailyシートのdate列がDate型に化けて日付比較が全て失敗していた既知の不具合:
setup()で列を「プレーンテキスト」書式(@)にしていても、appendRow()など
API経由で書き込んだ日付文字列("2026-09-20")は、Googleスプレッドシート側の
自動判定によりDate型のセルとして保存されることがある。これにより
getTodayStats/getDashboardData/aggregateDaily/deleteOldRawRows_の
String(row.date) === 対象日という文字列比較が常に不一致となり、当日分PV/UUが
0と表示されたり、日次集計・90日超データ削除が機能しなくなっていた。
dashboard.gsにnormalizeDate_()を追加し、readSheetAsObjects_()が返す
date列をDate型でも文字列でも常にyyyy-MM-dd形式に正規化するよう修正済み
(collector.gsや既存データの書き換えは不要)。createDailyTriggerが反映されておらず日次集計が全く動いていなかった事例: セットアップ手順3-11でcreateDailyTriggerを実行したはずだったが、実際にはトリガーが1つも登録されておらず(GASエディタ左の時計アイコン「トリガー」で確認可能)、dailyシートがずっと空のままだった。rawは90日間残るため過去分データは失われていなかったが、daily(グラフ・表用の日次集計)だけが埋まっていなかった。対処: createDailyTriggerを再実行してトリガーを登録し、aggregateForDate_(targetDate)(aggregateDailyから抽出した内部関数)を過去の各日付に対して手動実行してdailyを遡って埋めた。createDailyTrigger実行後は、GASエディタの「トリガー」画面で実際に1件登録されているか必ず目視確認すること(関数実行が完了しただけでは登録された保証にならない)。getSiteList() が daily シートのみを参照していたため、aggregateDaily が
一度も走っていない初日(daily が空)はプルダウンに何も表示されず、テーブルも
グラフも描画されなかった。getSiteList() を daily と raw の両方から siteId を
集めて重複除去するよう修正済み。dashboard.gs を修正した場合は、GASエディタに
貼り付けて保存した後、「デプロイ」→「デプロイを管理」→ 既存デプロイの編集アイコン→
バージョン「新バージョン」→デプロイ、で必ず再デプロイすること(URLは変わらない)。