【コピペで動く】GASでSearch ConsoleとGA4をスプレッドシートに自動連携!リライト候補を自動抽出する手順

ブログやWebメディアの記事数が20本、30本と増えてくると、「どの記事を優先してリライトすべきか?」「検索順位が8〜20位(2ページ目)でくすぶっている惜しいキーワードはどれか?」を毎回Search ConsoleとGA4(Googleアナリティクス4)の両方を開いて手動で突き合わせるのは非常に手間がかかります。
前回の記事ではLooker Studioを使ってブラウザ上で視覚的に確認するダッシュボードを構築しましたが、「毎週決まった曜日にデータを自動でスプレッドシートへ蓄積し、条件に合う記事へ自動で『🔥最優先リライト』とフラグを立てる仕組み」を作っておけば、日々の分析作業そのものを完全に自動化できます。
さらに、スプレッドシートに生のテキストデータとして集約されていれば、その表をそのままAI(ChatGPTやClaude、Difyなど)に読み込ませて具体的なタイトル改善案や追加見出しを作らせることも一瞬です。
本記事では、外部の有料アドオンや複雑なGCP(Google Cloud)の認証キー発行を一切使わず、Googleスプレッドシート標準のGAS(Google Apps Script)だけでSearch ConsoleとGA4のデータを自動集約する完全手順を、コピペで動くコード付きで解説します。
なぜLooker Studioだけでなく「GAS × スプレッドシート自動集約」が必要なのか?
「Looker Studioでグラフや表が見られるなら、わざわざスプレッドシートにデータを引っ張る必要はないのでは?」と思うかもしれません。しかし、実際のサイト運営では「人間が目で見て確認する画面(Looker Studio)」と「AIやプログラムで加工・判定するためのデータ置き場(スプレッドシート)」の2つを役割分担させるのが最も効率的です。
| 比較項目 | Looker Studio(視覚化ダッシュボード) | GAS × スプレッドシート(自動データ集約) |
|---|---|---|
| 主な役割 | 全体の推移やヒートマップを直感的に把握する | 条件判定による自動フラグ付け・AIへのデータ受け渡し |
| Search Console × GA4の結合 | データ統合の設定がやや複雑で動作が重くなりやすい | GASコード内でURLパスをキーにして高速・確実に結合できる |
| AI・外部ツール連携 | 画面上の表を毎回CSV出力する手間がかかる | Difyやn8n、GASから直接シートの行を読み取って自動処理できる |
💡 視覚的なSEO分析ダッシュボードをまだ作っていない方はこちら:
・【完全無料】Search Console × Looker StudioでSEO分析ダッシュボードを作る手順!リライト候補を自動抽出
この記事で実装する「自動リライト判定シート」の完成イメージ
今回作成するGASを動かすと、過去28日間のSearch Consoleデータ(記事URL・検索クエリ・表示回数・クリック数・CTR・平均順位)とGA4データ(ページ別PV数)が1つのシートに結合され、右端の列に以下のルールで「判定フラグ」が自動出力されます。
- 🔥最優先リライト(2ページ目・高露出): 平均順位が8位〜20位 かつ 表示回数が50回以上のクエリ(少し追記するだけで1ページ目に浮上しやすいお宝候補)
- ✏️タイトル改善(高表示・低CTR): 表示回数が100回以上あるのに、CTR(クリック率)が2%未満のクエリ
- 👑エース記事(順位キープ): 平均順位が5位以内 かつ クリック数が10回以上の主力クエリ
【事前準備】スプレッドシートの作成とGAS「Google Analytics Data API」の追加手順
それでは実際に手を動かして構築していきましょう。所要時間は約10分です。
STEP 1:スプレッドシートを作成し、シート名と1行目の見出しを設定する
- 新しいGoogleスプレッドシートを作成します(または既存のブログ管理用スプレッドシートを開きます)。
- 画面下部のシート(タブ)名をダブルクリックし、
SEO_RawDataという名前に変更します(※後述するGASコードがこのシート名を探してデータを書き込みます)。 - 1行目(A1〜H1セル)に、以下の通り見出しを入力します。
| A列 | B列 | C列 | D列 | E列 | F列 | G列 | H列 |
|---|---|---|---|---|---|---|---|
| 記事URL | 検索クエリ | 表示回数 | クリック数 | CTR | 平均掲載順位 | GA4 PV数 | 判定フラグ |
STEP 2:Apps Scriptを開き「Google Analytics Data API」を追加する
続いて、スプレッドシートからGA4のデータを直接取得するための公式サービスを有効化します。
- スプレッドシートの上部メニューから「拡張機能」➔「Apps Script」をクリックし、GASのエディタ画面を開きます。
- 画面左側のメニューにある「サービス」の横の「+」ボタンをクリックします。
- 「サービスを追加」という一覧ウィンドウが開くので、リストをスクロールして「Google Analytics Data API」を選択します。
- バージョンが
v1beta、ID(識別子)がAnalyticsDataになっていることを確認し、右下の青い「追加」ボタンをクリックします。
これで、左メニューの「サービス」の下に AnalyticsData が表示され、GASからGA4の数値を引っ張る準備が整いました。次のセクションでは、多くの人がつまずく「Search Console APIの連携設定」と実際のスクリプトコードを解説します。
【重要】「Search Console APIがない!」を解決するマニフェスト(appsscript.json)設定
Google Apps Script(GAS)でスプレッドシートやGmailなどを操作するコードを書く際、エディタ画面の左メニューにある「サービスを追加」から様々なGoogle公式API(Sheets、Gmail、AnalyticsDataなど)を簡単に追加できます。
しかし、ここで多くの開発者やブロガーが最初にぶつかる大きな罠が、「サービスを追加」のリストの中に『Google Search Console API』が存在しないという仕様の壁です。
古い解説記事やAIの誤った情報を見ると「拡張サービスからSearch Consoleを追加する」と書かれていることがありますが、現在のGASの標準機能一覧には含まれていません。この問題を解決し、GASから直接Search Consoleのデータを安全に引っ張るためには、隠し設定ファイルであるマニフェストファイル(appsscript.json)を直接編集してOAuthスコープ(権限)を明示的に定義する必要があります。
ステップ1:appsscript.jsonファイルを表示させる
- Apps Scriptのエディタ画面を開き、左側メニューの一番下にある「歯車アイコン(プロジェクトの設定)」をクリックします。
- 設定画面の中ほどにある「「appsscript.json」マニフェスト ファイルをエディタで表示する」のチェックボックスをONにします。
- 画面左側のメニュー上部にある「エディタ(
<>アイコン)」をクリックしてコード一覧に戻ると、ファイル一覧にappsscript.jsonが新たに出現します。
STEP 2:マニフェストファイルにSearch Consoleの権限を追加する
新しく現れた appsscript.json をクリックし、中身を以下のコードに丸ごと書き換えて、上部の「保存(フロッピーアイコン)」をクリックします。
{
"timeZone": "Asia/Tokyo",
"dependencies": {
"enabledAdvancedServices": [
{
"userSymbol": "AnalyticsData",
"version": "v1beta",
"serviceId": "analyticsdata"
}
]
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/webmasters.readonly",
"https://www.googleapis.com/auth/analytics.readonly",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/script.external_request"
]
}
この設定を行うことで、GASのスクリプト実行時にGoogle側から「Search Consoleのデータを読み取る権限(webmasters.readonly)」および「GA4・スプレッドシート・外部通信の権限」が安全に付与され、APIを直接叩けるようになります。
【コピペで動く】Search Console × GA4自動集約&リライト判定GASコードとトリガー設定
マニフェストの設定が完了したら、いよいよメインの自動集約スクリプトを組み込みます。
STEP 3:コード(コード.gs)を貼り付けてテスト実行する
元の `コード.gs` ファイルを開き、以下の完全版コードをすべてコピー&ペーストして保存します。
/**
* Core Media Lab: Search Console × GA4 自動集約 & リライト判定スクリプト
*/
const CONFIG = {
// Search ConsoleのサイトURL(ドメインプロパティの場合は "sc-domain:ドメイン名")
SEARCH_CONSOLE_SITE_URL: 'sc-domain:coremedialab.com',
// GA4のプロパティID(GA4の「管理」➔「プロパティ設定」にある9〜10桁の数字。未設定なら空欄でもOK)
GA4_PROPERTY_ID: 'YOUR_GA4_PROPERTY_ID',
// 集計期間(直近28日間)
DAYS_AGO: 28,
// 出力先シート名
SHEET_NAME: 'SEO_RawData'
};
function syncSeoAndGa4Data() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(CONFIG.SHEET_NAME);
if (!sheet) {
throw new Error(`シート「${CONFIG.SHEET_NAME}」が見つかりません。シート名を確認してください。`);
}
const today = new Date();
const endDate = Utilities.formatDate(new Date(today.getTime() - 2 * 24 * 60 * 60 * 1000), 'JST', 'yyyy-MM-dd');
const startDate = Utilities.formatDate(new Date(today.getTime() - (CONFIG.DAYS_AGO + 2) * 24 * 60 * 60 * 1000), 'JST', 'yyyy-MM-dd');
Logger.log(`集計期間: ${startDate} 〜 ${endDate}`);
// 1. Search Console データの取得
const scData = fetchSearchConsoleData(startDate, endDate);
// 2. GA4 データの取得
const ga4DataMap = fetchGa4PageViews(startDate, endDate);
// 3. データの結合と判定フラグ付与
const rows = [];
scData.forEach(item => {
const pageUrl = item.page;
const query = item.query;
const impressions = item.impressions;
const clicks = item.clicks;
const ctr = item.ctr;
const position = Math.round(item.position * 10) / 10;
const ga4Pv = matchGa4Pv(pageUrl, ga4DataMap);
// リライト優先度の自動判定ロジック
let flag = '様子見';
if (position >= 8 && position <= 20 && impressions >= 50) {
flag = '🔥最優先リライト(2ページ目・高露出)';
} else if (impressions >= 100 && ctr < 0.02) {
flag = '✏️タイトル改善(高表示・低CTR)';
} else if (position <= 5 && clicks >= 10) {
flag = '👑エース記事(順位キープ)';
}
rows.push([
pageUrl,
query,
impressions,
clicks,
Utilities.formatString('%.2f%%', ctr * 100),
position,
ga4Pv,
flag
]);
});
// 4. シートへの一括書き出し
if (sheet.getLastRow() > 1) {
sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).clearContent();
}
if (rows.length > 0) {
sheet.getRange(2, 1, rows.length, rows[0].length).setValues(rows);
Logger.log(`書き込み完了: ${rows.length} 件`);
} else {
Logger.log('該当データがありませんでした。');
}
}
/**
* Search Console APIへ直接リクエストを投げてデータ取得
*/
function fetchSearchConsoleData(startDate, endDate) {
const endpoint = `https://www.googleapis.com/webmasters/v3/sites/${encodeURIComponent(CONFIG.SEARCH_CONSOLE_SITE_URL)}/searchAnalytics/query`;
const payload = {
startDate: startDate,
endDate: endDate,
dimensions: ['page', 'query'],
rowLimit: 1000,
type: 'web'
};
const options = {
method: 'post',
contentType: 'application/json',
headers: {
Authorization: `Bearer ${ScriptApp.getOAuthToken()}`
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
try {
const response = UrlFetchApp.fetch(endpoint, options);
const result = JSON.parse(response.getContentText());
if (result.error) {
Logger.log(`Search Console API エラー: ${result.error.message}`);
return [];
}
if (!result.rows) return [];
return result.rows.map(row => ({
page: row.keys[0],
query: row.keys[1],
clicks: row.clicks,
impressions: row.impressions,
ctr: row.ctr,
position: row.position
}));
} catch (e) {
Logger.log(`通信エラー: ${e.message}`);
return [];
}
}
/**
* GA4 APIからページ別PVを取得
*/
function fetchGa4PageViews(startDate, endDate) {
const pvMap = new Map();
if (!CONFIG.GA4_PROPERTY_ID || CONFIG.GA4_PROPERTY_ID === 'YOUR_GA4_PROPERTY_ID') {
return pvMap;
}
try {
const request = {
dateRanges: [{ startDate: startDate, endDate: endDate }],
dimensions: [{ name: 'pagePath' }],
metrics: [{ name: 'screenPageViews' }],
limit: 1000
};
const response = AnalyticsData.Properties.runReport(request, `properties/${CONFIG.GA4_PROPERTY_ID}`);
if (response.rows) {
response.rows.forEach(row => {
const path = row.dimensionValues[0].value;
const pvs = parseInt(row.metricValues[0].value, 10);
pvMap.set(path, pvs);
});
}
} catch (e) {
Logger.log(`GA4 API エラー: ${e.message}`);
}
return pvMap;
}
function matchGa4Pv(fullUrl, ga4Map) {
try {
const urlParts = fullUrl.split('//')[1];
const path = urlParts.substring(urlParts.indexOf('/'));
return ga4Map.get(path) || 0;
} catch (e) {
return 0;
}
}
⚠️ よくあるエラー:「myFunction を実行しようとしましたが、削除されました」と出たら?
GASのエディタ上部ツールバーにある関数選択ドロップダウン(実行ボタンのすぐ左)が、古いデフォルト関数のmyFunctionのままになっていることが原因です。
ドロップダウンをクリックしてsyncSeoAndGa4Dataに切り替えてから「実行」ボタンを押してください。
STEP 4:初回実行とアクセスの承認
- 上部メニューの実行関数が `syncSeoAndGa4Data` になっていることを確認し、「実行」をクリックします。
- 初回のみ「承認が必要です」というポップアップが出るため、「権限を確認」➔ 自分のGoogleアカウントを選択 ➔「詳細」をクリック ➔「無題のプロジェクト(安全ではないページ)に移動」をクリック ➔「許可」の順に進めます。
- スプレッドシートの `SEO_RawData` タブに戻り、記事データと判定フラグがきれいに書き込まれていれば成功です!
STEP 5:毎週自動実行する「トリガー」の設定
- GASエディタの左側メニューにある「時計アイコン(トリガー)」をクリックします。
- 右下の「トリガーを追加」をクリックします。
- 以下の条件を設定して「保存」します。
- 実行する関数: `syncSeoAndGa4Data`
- イベントのソース:
時間主導型 - 時間ベースのトリガーのタイプ:
週ベースのタイマー - 曜日:
毎週月曜日 - 時刻:
午前5時〜6時
これで、毎週月曜日の早朝にGoogleのサーバーが自動でSearch ConsoleとGA4のデータを回収し、スプレッドシートを最新の状態に保ってくれます。
まとめ:集約したSEOデータをAIに読ませて「リライト案作成」まで半自動化しよう
本記事では、Google Apps Script(GAS)を使ってSearch ConsoleとGA4のデータをスプレッドシートへ自動集約し、リライト優先度を自動判定する仕組みの構築手順を解説しました。
- APIの直接叩き方: 拡張サービスにないSearch Console APIは、マニフェスト(
appsscript.json)にOAuthスコープを追加してUrlFetchAppで安全に取得する。 - データと役割の分担: 視覚的な確認には前回の「Looker Studio」を使い、条件判定やAI連携には今回の「スプレッドシート自動集約」を組み合わせることで分析効率が最大化する。
- 完全自動化の実現: 週次トリガーを組むことで、手動でのデータ集計作業がゼロになり、いつでも「今リライトすべき記事」が即座に分かる環境が整う。
スプレッドシートにデータが綺麗に溜まるようになれば、毎週月曜日にそのデータをコピーしてこのチャット(AI)に貼り付けるだけで、「この記事のH2見出しをこう書き換えましょう」「この記事にこういう補足を追加すれば1ページ目に入ります」という具体的なリライト案を一瞬で生成できるようになります。
当メディアでは、こうしたAPIやGAS、Difyを活用した個人開発・ブログ運営の効率化ノウハウを多数発信する予定です。ぜひ他の自動化記事も参考に、あなたのメディア運用基盤をさらにブラッシュアップしてみてください。