Webサイトの検索順位やアクセスを伸ばすためには、日々のデータをもとに「どの記事が伸びているか」「どの記事に改善余地があるか」を把握し、仮説を立てて施策を検証するPDCAサイクルが不可欠です。しかし、高額なSEOツールや外部サーバーを契約するのはハードルが高いと感じる方も多いのではないでしょうか。本記事では、外部の有料ツールやサーバーを一切使わず、Google Apps Script(GAS)とGoogleスプレッドシートのみを活用して、Search Console(GSC)とGoogleアナリティクス4(GA4)の日次データを完全無料で自動同期・分析・仮説検証する環境構築手順を詳しく解説します。
完全無料で実現する日次分析・仮説検証の全体像
外部サーバーやクラウド有料機能を使わず、Googleの無料標準サービスのみを組み合わせて自動化を実現するアーキテクチャです。
完全無料アーキテクチャの構成要素
- データ収集エンジン:Google Apps Script(GAS)
Googleが提供するサーバーレス実行環境です。完全無料で稼働し、毎朝指定した時刻に自動でAPIを実行します。 - データソース:Search Console API & Google Analytics Data API
どちらもGoogle公式のAPIであり、日次の集計データ取得であれば無料枠内で余裕をもって運用可能です。 - データ蓄積・管理:Googleスプレッドシート
日次のKPI推移、施策ログ、仮説検証のBefore/After判定を1つのシートで一元管理します。 - 日次分析・仮説検証:定期チェックとAI活用
蓄積された日次データを毎朝確認し、急変動の検知やタイトル修正などの改善仮説を立案・記録します。
統合管理シートのデータ構造
本仕組みでは、スプレッドシートの1行で「検索流入(GSC)」と「サイト内行動(GA4)」を横断して把握できるように設計します。
日次KPI推移シートの管理項目
- 日付:対象の日次データの日付(確定値となる3日前を推奨)
- [GSC] 表示回数:Google検索結果にサイトが表示された総回数
- [GSC] クリック数:検索結果からクリックされた総回数
- [GSC] CTR (%):検索結果におけるクリック率(クリック数 ÷ 表示回数)
- [GSC] 平均掲載順位:検索結果におけるサイト全体の平均順位
- [GA4] 総セッション:サイト全体の総セッション数
- [GA4] Organic検索セッション:検索エンジンからの自然流入セッション数
- [GA4] アクティブユーザー:サイトを訪問したユニークユーザー数
- [GA4] エンゲージメント率 (%):10秒以上滞在または特定アクションを行ったセッションの割合
- [GA4] 平均滞在時間 (秒):ユーザー1人あたりの平均エンゲージメント時間
- 施策・特記事項メモ:リライト実施、新規記事公開、アルゴリズム変動などの記録
GASによる自動連携の環境構築手順
スプレッドシートを作成し、GASスクリプトを設定する具体的な手順です。所要時間は約5分で完了します。
管理用スプレッドシートの作成とApps Scriptの起動
- Googleドライブで新しいGoogleスプレッドシートを作成します(シート名例:「サーチコンソール日次分析・仮説検証管理シート」)。
- 上部メニューの「拡張機能」>「Apps Script」をクリックしてエディタを開きます。
APIサービスを2つ追加する設定
Apps ScriptからGoogleの各サービスを直接呼び出せるように、標準の「拡張サービス」を追加します。
- Apps Scriptエディタの左側メニューにある「サービス(+マーク)」をクリックします。
- 一覧から「Google Search Console API」を選択し、識別子を
SearchConsoleとして「追加」をクリックします。 - 再度「サービス(+マーク)」をクリックし、一覧から「Google Analytics Data API」を選択し、識別子を
AnalyticsDataとして「追加」をクリックします。
日次自動連携スクリプトの実装
エディタ内の コード.gs に以下のコードを貼り付けます。上部の CONFIG に対象サイトのURLとGA4のプロパティIDを設定してください。
/*
* Google Search Console & GA4 日次データ自動連携スクリプト(完全無料)
*/
const CONFIG = {
GSC_SITE_URL: 'https://eguweb.jp/', // Search ConsoleプロパティURL (ドメインプロパティの場合は 'sc-domain:eguweb.jp')
GA4_PROPERTY_ID: '123456789', // ご自身のGA4プロパティID(数字のみ)
SHEET_NAME: '日次KPI推移', // 書き込み先シート名
DAYS_AGO: 3 // データ確定ラグ(2〜3日前が確定値)
};
function fetchDailyGSCandGA4Data() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getSheetByName(CONFIG.SHEET_NAME);
// シートが存在しない場合はヘッダー付きで新規作成
if (!sheet) {
sheet = ss.insertSheet(CONFIG.SHEET_NAME);
sheet.appendRow([
'日付',
'[GSC] 表示回数',
'[GSC] クリック数',
'[GSC] CTR (%)',
'[GSC] 平均掲載順位',
'[GA4] 総セッション',
'[GA4] Organic検索セッション',
'[GA4] アクティブユーザー',
'[GA4] エンゲージメント率 (%)',
'[GA4] 平均滞在時間 (秒)',
'施策・特記事項メモ'
]);
sheet.getRange(1, 1, 1, 11).setBackground('#0284c7').setFontColor('#ffffff').setFontWeight('bold');
sheet.setFrozenRows(1);
}
// 対象日付(3日前)の算出
const targetDateObj = new Date();
targetDateObj.setDate(targetDateObj.getDate() - CONFIG.DAYS_AGO);
const targetDateStr = Utilities.formatDate(targetDateObj, Session.getScriptTimeZone(), 'yyyy-MM-dd');
Logger.log('取得対象日: ' + targetDateStr);
// 1. Google Search Console APIからデータ取得
let gscImpressions = 0;
let gscClicks = 0;
let gscCtr = 0;
let gscPosition = 0;
try {
const gscRequest = {
startDate: targetDateStr,
endDate: targetDateStr,
dimensions: []
};
const gscResponse = SearchConsole.Searchanalytics.query(gscRequest, CONFIG.GSC_SITE_URL);
if (gscResponse.rows && gscResponse.rows.length > 0) {
const row = gscResponse.rows[0];
gscImpressions = row.impressions || 0;
gscClicks = row.clicks || 0;
gscCtr = row.ctr ? Number((row.ctr * 100).toFixed(2)) : 0;
gscPosition = row.position ? Number(row.position.toFixed(1)) : 0;
}
} catch (e) {
Logger.log('GSC取得エラー: ' + e.message);
}
// 2. Google Analytics 4 Data APIからデータ取得
let gaSessions = 0;
let gaOrganicSessions = 0;
let gaUsers = 0;
let gaEngagementRate = 0;
let gaAvgDuration = 0;
if (CONFIG.GA4_PROPERTY_ID && CONFIG.GA4_PROPERTY_ID !== 'YOUR_GA4_PROPERTY_ID') {
try {
// サイト全体指標
const gaRequest = AnalyticsData.newRunReportRequest();
gaRequest.dateRanges = [{ startDate: targetDateStr, endDate: targetDateStr }];
gaRequest.metrics = [
{ name: 'sessions' },
{ name: 'activeUsers' },
{ name: 'engagementRate' },
{ name: 'userEngagementDuration' }
];
const gaResponse = AnalyticsData.Properties.runReport(gaRequest, 'properties/' + CONFIG.GA4_PROPERTY_ID);
if (gaResponse.rows && gaResponse.rows.length > 0) {
const m = gaResponse.rows[0].metricValues;
gaSessions = parseInt(m[0].value || '0', 10);
gaUsers = parseInt(m[1].value || '0', 10);
const engRateRaw = parseFloat(m[2].value || '0');
gaEngagementRate = Number((engRateRaw * 100).toFixed(2));
const totalDuration = parseFloat(m[3].value || '0');
gaAvgDuration = gaUsers > 0 ? Number((totalDuration / gaUsers).toFixed(1)) : 0;
}
// Organic Search流入のみ抽出
const gaOrganicRequest = AnalyticsData.newRunReportRequest();
gaOrganicRequest.dateRanges = [{ startDate: targetDateStr, endDate: targetDateStr }];
gaOrganicRequest.dimensions = [{ name: 'sessionDefaultChannelGroup' }];
gaOrganicRequest.metrics = [{ name: 'sessions' }];
gaOrganicRequest.dimensionFilter = {
filter: {
fieldName: 'sessionDefaultChannelGroup',
stringFilter: { value: 'Organic Search', matchType: 'EXACT' }
}
};
const gaOrganicResponse = AnalyticsData.Properties.runReport(gaOrganicRequest, 'properties/' + CONFIG.GA4_PROPERTY_ID);
if (gaOrganicResponse.rows && gaOrganicResponse.rows.length > 0) {
gaOrganicSessions = parseInt(gaOrganicResponse.rows[0].metricValues[0].value || '0', 10);
}
} catch (e) {
Logger.log('GA4取得エラー: ' + e.message);
}
}
// 重複確認と更新/追記
const data = sheet.getDataRange().getValues();
let rowToUpdate = -1;
for (let i = 1; i < data.length; i++) {
const existingDate = data[i][0];
const formattedExisting = existingDate instanceof Date
? Utilities.formatDate(existingDate, Session.getScriptTimeZone(), 'yyyy-MM-dd')
: String(existingDate).trim();
if (formattedExisting === targetDateStr) {
rowToUpdate = i + 1;
break;
}
}
const rowData = [
targetDateStr,
gscImpressions,
gscClicks,
gscCtr,
gscPosition,
gaSessions,
gaOrganicSessions,
gaUsers,
gaEngagementRate,
gaAvgDuration,
''
];
if (rowToUpdate > 0) {
sheet.getRange(rowToUpdate, 1, 1, rowData.length).setValues([rowData]);
Logger.log('既存行を更新しました: ' + rowToUpdate);
} else {
sheet.appendRow(rowData);
Logger.log('新規追記完了: ' + targetDateStr);
}
}
初回実行とアクセスの許可
- 上部ツールバーの関数名で
fetchDailyGSCandGA4Dataを選択し、「実行」をクリックします。 - 初回実行時は「承認が必要です」というダイアログが表示されるため、「権限を確認」をクリックして自身のアカウントを選択し、アクセスを許可します。
- 実行完了後、スプレッドシートを開いて「日次KPI推移」シートに3日前のデータが正しく反映されていることを確認します。
毎朝の自動実行タイマートリガーの設定
- Apps Scriptエディタの左側メニューにある「トリガー(時計マーク)」をクリックします。
- 右下の「+ トリガーを追加」をクリックし、以下のように設定します。
- 実行する関数:
fetchDailyGSCandGA4Data - イベントのソース:
時間主導型 - 時間ベースのトリガーのタイプ:
日ベースのタイマー - 時刻を選択:
午前6時〜7時
- 実行する関数:
- 「保存」をクリックすれば、毎朝自動で最新データがスプレッドシートへ追記されます。
日次分析と仮説検証の運用サイクル
データが自動蓄積される環境ができたら、毎朝のルーティンとして以下の4ステップで仮説検証PDCAを回します。
仮説検証PDCAの4ステップ
- 日次KPIの定点観測
前日比(DoD)や前週同曜日比(WoW)で、検索クリック数やOrganicセッションの増減傾向をチェックします。 - 変動・改善機会の検知
・急落/急上昇:掲載順位や表示回数が大きく動いた記事を特定。
・高表示・低CTR:検索結果で多く見られているのにクリックされていない記事(タイトル改善の最優先対象)。
・11位〜20位のポテンシャルクエリ:2ページ目に位置し、少しのリライトで1ページ目(10位以内)へ急伸可能なクエリ。 - 改善仮説の立案・実行
「どの課題に対して」「何を変更することで」「どの指標がどう変わるか」を仮説ログに記録し、リライトを実行します(例:「タイトル冒頭に【完全無料】を追記し、CTRを3.2%から5.0%へ引き上げる」)。 - 効果検証と振り返り
施策反映後(3日後・7日後・14日後)の数値を追跡し、仮説の合否判定と次のアクションを決定します。
完全無料運用における重要注意点
本手法を長期間安定して運用するために、以下の注意点と仕様を把握しておきましょう。
運用のポイントと制約
- データの確定タイムラグ(2〜3日遅れ)
Search ConsoleおよびGA4の集計データは、リアルタイムではなく2〜3日かけて確定します。当日の日次バッチでは「3日前」のデータを対象にすることで、ブレのない確定数値を記録できます。 - GA4プロパティIDの確認方法
GA4のプロパティIDは、Googleアナリティクス管理画面 >「プロパティ設定」>「プロパティの詳細」に記載されている9〜10桁の「数字のみ」を入力してください(測定ID「G-XXXXXXX」ではありません)。 - API無料利用枠の範囲
Google Apps Scriptおよび各APIサービスは完全無料で利用できます。GASの標準実行時間制限(1回あたり最大6分)がありますが、日次の集計データ取得処理は数秒で終了するため問題ありません。 - プロパティURLの指定形式
Search Consoleで「URLプレフィックス」で登録している場合はhttps://example.com/、「ドメインプロパティ」で登録している場合はsc-domain:example.comの形式で記述してください。
まとめ
Google Apps Scriptとスプレッドシートを組み合わせることで、高価な外部ツールやサーバー構築費をかけることなく、Search ConsoleとGA4を統合した強力なSEO分析環境を完全無料で構築できます。
毎朝の自動記録によってサイトの微細な変化を逃さず捉え、データに基づいた確実な改善PDCAを回していきましょう。
