【コピペで動く】GA4 + Search ConsoleのデータをTypeScriptで自動取得する方法
GA4やSearch ConsoleのCSVを毎回手動でエクスポートしていませんか?
結論: TypeScript 2ファイル + GitHub Actionsで週1完全自動化できます。 Service Accountの設定に15分、コードはコピペで動きます。
私の体験談
筆者はAIエージェント11体を運用しており、SEO分析もエージェントに任せています。エージェントがデータを分析するには、CSVが自動で更新されている必要がありました。この仕組みで手動作業がゼロになりました。
全体像
GitHub Actions(毎週月曜自動実行)
├── search_console_tracker.ts → search-console-latest.csv
└── ga4_tracker.ts → ga4-pages-latest.csv
ga4-acquisition-latest.csv
取得するデータ:
- Search Console: 検索クエリ別のクリック数・表示回数・CTR・掲載順位(過去28日)
- GA4: ページ別PV・滞在時間・直帰率 + 流入元別セッション数(過去28日)
事前準備: Service Account設定(15分)
1. Google Cloudプロジェクト作成
Google Cloud Console にアクセスし、新しいプロジェクトを作成します。
2. APIを有効化
「APIとサービス」→「ライブラリ」で以下を有効化:
- Google Search Console API
- Google Analytics Data API
3. Service Account作成
「認証情報」→「+認証情報を作成」→「サービスアカウント」
サービスアカウント名は任意(例: analytics-reader)。作成後、メールアドレスをコピーしておきます。
4. JSONキーを発行
サービスアカウントの「キー」タブ→「鍵を追加」→「JSON」。ダウンロードされたJSONが認証キーです。
5. アクセス権を付与
Search Console: 設定→ユーザーと権限→サービスアカウントを「制限付き」で追加 GA4: 管理→プロパティのアクセス管理→サービスアカウントを「閲覧者」で追加
実装①: Search Console Tracker
// search_console_tracker.ts
import { google } from "googleapis";
import { writeFileSync, mkdirSync, existsSync } from "fs";
const SERVICE_ACCOUNT_JSON = process.env.GOOGLE_SERVICE_ACCOUNT_JSON ?? "";
const SITE_URL = process.env.SEARCH_CONSOLE_SITE_URL ?? "https://masatoman.net";
const OUTPUT_DIR = process.env.ANALYTICS_OUTPUT_DIR || "./analytics";
const OUTPUT_FILE = `${OUTPUT_DIR}/search-console-latest.csv`;
function getClient() {
const credentials = JSON.parse(SERVICE_ACCOUNT_JSON);
const auth = new google.auth.GoogleAuth({
credentials,
scopes: ["https://www.googleapis.com/auth/webmasters.readonly"],
});
return google.searchconsole({ version: "v1", auth });
}
async function main() {
const client = getClient();
const now = new Date();
const endDate = new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000);
const startDate = new Date(endDate.getTime() - 28 * 24 * 60 * 60 * 1000);
const fmt = (d: Date) => d.toISOString().split("T")[0];
const res = await client.searchanalytics.query({
siteUrl: SITE_URL,
requestBody: {
startDate: fmt(startDate),
endDate: fmt(endDate),
dimensions: ["query"],
rowLimit: 500,
},
});
const rows = res.data.rows ?? [];
if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true });
const header = "Query,Clicks,Impressions,CTR,Position";
const lines = rows.map((row) => {
const query = row.keys?.[0] ?? "";
return `"${query.replace(/"/g, '""')}",${row.clicks},${row.impressions},${((row.ctr ?? 0) * 100).toFixed(2)}%,${(row.position ?? 0).toFixed(1)}`;
});
writeFileSync(OUTPUT_FILE, [header, ...lines].join("\n"), "utf-8");
console.log(`Saved ${rows.length} queries to ${OUTPUT_FILE}`);
}
main();
実装②: GA4 Tracker
// ga4_tracker.ts
import { google } from "googleapis";
import { writeFileSync, mkdirSync, existsSync } from "fs";
const SERVICE_ACCOUNT_JSON = process.env.GOOGLE_SERVICE_ACCOUNT_JSON ?? "";
const PROPERTY_ID = process.env.GA4_PROPERTY_ID ?? "";
const OUTPUT_DIR = process.env.ANALYTICS_OUTPUT_DIR || "./analytics";
function getClient() {
const credentials = JSON.parse(SERVICE_ACCOUNT_JSON);
const auth = new google.auth.GoogleAuth({
credentials,
scopes: ["https://www.googleapis.com/auth/analytics.readonly"],
});
return google.analyticsdata({ version: "v1beta", auth });
}
async function main() {
const client = getClient();
// ページ別レポート
const pagesRes = await client.properties.runReport({
property: `properties/${PROPERTY_ID}`,
requestBody: {
dateRanges: [{ startDate: "28daysAgo", endDate: "yesterday" }],
dimensions: [{ name: "pagePath" }],
metrics: [
{ name: "screenPageViews" },
{ name: "averageSessionDuration" },
{ name: "bounceRate" },
],
orderBys: [{ metric: { metricName: "screenPageViews" }, desc: true }],
limit: 100,
},
});
if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true });
const pagesRows = pagesRes.data.rows ?? [];
const pagesHeader = "Page path,Views,Avg engagement time (sec),Bounce rate";
const pagesLines = pagesRows.map((row) => {
const path = row.dimensionValues?.[0]?.value ?? "";
const views = row.metricValues?.[0]?.value ?? "0";
const avgTime = parseFloat(row.metricValues?.[1]?.value ?? "0").toFixed(1);
const bounce = (parseFloat(row.metricValues?.[2]?.value ?? "0") * 100).toFixed(1);
return `"${path}",${views},${avgTime},${bounce}%`;
});
writeFileSync(`${OUTPUT_DIR}/ga4-pages-latest.csv`, [pagesHeader, ...pagesLines].join("\n"), "utf-8");
// 集客レポート
const acqRes = await client.properties.runReport({
property: `properties/${PROPERTY_ID}`,
requestBody: {
dateRanges: [{ startDate: "28daysAgo", endDate: "yesterday" }],
dimensions: [{ name: "sessionSourceMedium" }],
metrics: [{ name: "sessions" }, { name: "conversions" }],
orderBys: [{ metric: { metricName: "sessions" }, desc: true }],
limit: 50,
},
});
const acqRows = acqRes.data.rows ?? [];
const acqHeader = "Source / Medium,Sessions,Conversions";
const acqLines = acqRows.map((row) => {
const source = row.dimensionValues?.[0]?.value ?? "";
return `"${source}",${row.metricValues?.[0]?.value ?? 0},${row.metricValues?.[1]?.value ?? 0}`;
});
writeFileSync(`${OUTPUT_DIR}/ga4-acquisition-latest.csv`, [acqHeader, ...acqLines].join("\n"), "utf-8");
console.log(`Pages: ${pagesRows.length} rows, Acquisition: ${acqRows.length} rows`);
}
main();
自動化: GitHub Actions
# .github/workflows/analytics-tracker.yml
name: Analytics Tracker
on:
schedule:
- cron: '0 1 * * 1' # 毎週月曜 10:00 JST
workflow_dispatch:
permissions:
contents: write
jobs:
track:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci || npm install
- run: mkdir -p analytics-output
- name: Search Console
env:
GOOGLE_SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
SEARCH_CONSOLE_SITE_URL: https://masatoman.net
ANALYTICS_OUTPUT_DIR: ./analytics-output
run: npx tsx search_console_tracker.ts
- name: GA4
env:
GOOGLE_SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
GA4_PROPERTY_ID: ${{ secrets.GA4_PROPERTY_ID }}
ANALYTICS_OUTPUT_DIR: ./analytics-output
run: npx tsx ga4_tracker.ts
- name: Commit CSVs
run: |
git config user.name "analytics-bot"
git config user.email "bot@example.com"
git add analytics-output/*.csv
git diff --cached --quiet || git commit -m "data: update analytics CSVs"
git push
必要なGitHub Secrets:
GOOGLE_SERVICE_ACCOUNT_JSON— Service AccountのJSON全文GA4_PROPERTY_ID— GA4プロパティID(数字のみ)
応用: AIにSEO分析させる
CSVが自動更新されるので、Claude Codeに以下のように指示するだけで分析できます:
analytics/search-console-latest.csv を読んで、クリック数は多いが掲載順位が10位以下のキーワードをリストアップして。それらを元に、次に書くべき記事のタイトル案を3つ出して。
analytics/ga4-pages-latest.csv を読んで、PVは高いが滞在時間が短い記事を特定して。その記事の構成改善案を出して。
まとめ
| ステップ | 所要時間 |
|---|---|
| Service Account設定 | 15分(初回のみ) |
| コードをコピペ | 2分 |
| GitHub Actionsで自動実行 | 0分(毎週自動) |
手動エクスポートは今日で卒業です。