Claude Codeでスプレッドシート連携を自動化する実務ガイド
Claude CodeでCSV集計、Google Sheets API、Apps Script連携を実装する実務手順と失敗回避策。
スプレッドシート連携は地味ですが、売上、問い合わせ、広告、在庫、請求の数字が集まる場所なので、壊れるとすぐ事業に響きます。
Claude Codeを使う価値は「表計算をAIに任せる」ことではありません。CSVを集計する小さなNode.jsスクリプト、Google Sheets APIで行を追加する処理、Apps Scriptで問い合わせを分類する処理を、既存リポジトリのルールに合わせて作らせ、差分と検証結果まで残せることです。
この記事では、初心者でも手元で試せるCSV集計から始め、Google Sheets API、Apps Script、Claude Codeへの依頼テンプレートまで一つの流れにします。関連する基本操作はClaude Code生産性Tipsと検証レシート運用を先に読むと理解しやすくなります。公式情報はClaude Code docs、Claude Code CLI usage、Google Sheets API Node.js quickstart、Apps Script Sheets guide、SheetJS docsを基準にしてください。
先に決める設計
最初に決めるのは、Claude Codeに何を作らせるかではなく、どのデータを正とするかです。スプレッドシート自動化では、同じ数字がCSV、Google Sheets、CRM、会計ソフトに散らばりがちです。どれが入力で、どれが集計結果で、どこから人間が修正してよいのかを決めないまま自動化すると、便利な処理ほど事故を増やします。
実務では次の3層に分けると扱いやすくなります。
| 層 | 役割 | 例 | Claude Codeに頼むこと |
|---|---|---|---|
| Raw | 人が触らない元データ | フォーム送信、決済CSV、広告CSV | 取り込みとバリデーション |
| Clean | 型をそろえた中間データ | 日付、金額、ステータスを整えた表 | 正規化、重複除去、エラー行の隔離 |
| Report | 人が読む集計 | 月次売上、問い合わせ優先度、広告KPI | 集計、グラフ用CSV、Slack通知 |
初心者が失敗しやすいのは、Reportシートに直接APIで追記するパターンです。人が見やすい列順、結合セル、色、メモは、機械が書きやすい形と相性が悪いです。APIで触るのはRawかCleanに限定し、Reportは関数、ピボット、Looker Studio、または別スクリプトで作るほうが安全です。
実例1: CSV売上を月次に集計する
まずはGoogle認証なしで動くCSV集計です。Claude CodeにいきなりAPI連携を頼む前に、ローカルで再現できる最小処理を作るとレビューが楽になります。
data/sales.csvを次の形で用意します。
date,channel,product,amount,status
2026-05-01,organic,Claude Code Cheatsheet,0,won
2026-05-02,gumroad,Prompt Template Pack,2980,won
2026-05-08,consultation,Team Workshop,120000,won
2026-05-11,gumroad,Prompt Template Pack,2980,refunded
2026-06-01,organic,Claude Code Cheatsheet,0,won
2026-06-02,consultation,Implementation Review,80000,won
scripts/summarize-sales.mjsとして保存します。
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
const inputPath = process.argv[2] ?? "data/sales.csv";
const outputPath = process.argv[3] ?? "out/monthly-summary.csv";
function parseCsvLine(line) {
const cells = [];
let current = "";
let inQuotes = false;
for (let index = 0; index < line.length; index += 1) {
const char = line[index];
const next = line[index + 1];
if (char === '"' && inQuotes && next === '"') {
current += '"';
index += 1;
continue;
}
if (char === '"') {
inQuotes = !inQuotes;
continue;
}
if (char === "," && !inQuotes) {
cells.push(current.trim());
current = "";
continue;
}
current += char;
}
cells.push(current.trim());
return cells;
}
function parseCsv(source) {
const lines = source.trim().split(/\r?\n/).filter(Boolean);
const headers = parseCsvLine(lines[0]);
return lines.slice(1).map((line) => {
const cells = parseCsvLine(line);
return Object.fromEntries(headers.map((header, index) => [header, cells[index] ?? ""]));
});
}
function toMonth(dateValue) {
const date = new Date(`${dateValue}T00:00:00Z`);
if (Number.isNaN(date.getTime())) {
throw new Error(`Invalid date: ${dateValue}`);
}
return dateValue.slice(0, 7);
}
const rows = parseCsv(await readFile(inputPath, "utf8"));
const summary = new Map();
for (const row of rows) {
if (row.status !== "won") continue;
const amount = Number(row.amount);
if (!Number.isFinite(amount)) {
throw new Error(`Invalid amount: ${JSON.stringify(row)}`);
}
const key = `${toMonth(row.date)},${row.channel}`;
const current = summary.get(key) ?? { month: toMonth(row.date), channel: row.channel, deals: 0, revenue: 0 };
current.deals += 1;
current.revenue += amount;
summary.set(key, current);
}
const output = [
"month,channel,deals,revenue",
...[...summary.values()]
.sort((a, b) => `${a.month}:${a.channel}`.localeCompare(`${b.month}:${b.channel}`))
.map((row) => `${row.month},${row.channel},${row.deals},${row.revenue}`),
].join("\n");
await mkdir(path.dirname(outputPath), { recursive: true });
await writeFile(outputPath, `${output}\n`, "utf8");
console.log(`Wrote ${outputPath} (${summary.size} groups)`);
実行します。
mkdir -p data out scripts
node scripts/summarize-sales.mjs data/sales.csv out/monthly-summary.csv
cat out/monthly-summary.csv
この例で大事なのは、失敗時に止まることです。金額が空、日付が壊れている、ステータス名が変わった、という状態で黙って0円として集計すると、月末に人間が見つけるしかありません。Claude Codeには「不正データは別ファイルに隔離する」「エラー内容を行番号付きで出す」まで頼むと運用に近づきます。
実例2: Google Sheets APIで問い合わせを追記する
次はサービスアカウントを使ってGoogle Sheetsに行を追加します。個人のOAuthでも動きますが、チーム運用では誰のアカウントで動いているかが曖昧になりやすいので、まずはサービスアカウントと共有設定を明示するほうが安全です。
準備は3つです。Google CloudでSheets APIを有効化し、サービスアカウントのJSONキーを作り、そのサービスアカウントのメールアドレスを対象スプレッドシートに編集者として共有します。シートにはRawタブを作り、1行目にcreatedAt,source,subject,amount,statusを置きます。
npm install googleapis
export GOOGLE_APPLICATION_CREDENTIALS="$PWD/service-account.json"
export SHEET_ID="your-google-sheet-id"
scripts/append-lead-to-sheet.mjsを作ります。
import { google } from "googleapis";
const { GOOGLE_APPLICATION_CREDENTIALS, SHEET_ID } = process.env;
if (!GOOGLE_APPLICATION_CREDENTIALS) {
throw new Error("GOOGLE_APPLICATION_CREDENTIALS is required");
}
if (!SHEET_ID) {
throw new Error("SHEET_ID is required");
}
const auth = new google.auth.GoogleAuth({
keyFile: GOOGLE_APPLICATION_CREDENTIALS,
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
});
const sheets = google.sheets({ version: "v4", auth });
const source = process.argv[2] ?? "web";
const subject = process.argv[3] ?? "Claude Code consultation";
const amount = Number(process.argv[4] ?? 0);
if (!Number.isFinite(amount)) {
throw new Error(`Invalid amount: ${process.argv[4]}`);
}
await sheets.spreadsheets.values.append({
spreadsheetId: SHEET_ID,
range: "Raw!A:E",
valueInputOption: "USER_ENTERED",
insertDataOption: "INSERT_ROWS",
requestBody: {
values: [[new Date().toISOString(), source, subject, amount, "new"]],
},
});
console.log("Appended lead row");
実行例です。
node scripts/append-lead-to-sheet.mjs newsletter "Spreadsheet automation review" 50000
Claude Codeにこの処理を作らせるときは、環境変数名、書き込み先タブ、列順、権限の境界を依頼文に入れます。認証情報を記事やリポジトリに貼らないことも明記してください。service-account.jsonをGit管理から外すため、.gitignore確認も一緒に依頼します。
実例3: Apps Scriptで売上と問い合わせを分類する
フォーム送信を受けて、スプレッドシート内で優先度を付けたい場合はApps Scriptが便利です。サーバーを用意せず、フォーム、Sheets、メール通知を近い場所で扱えます。ただしApps Scriptは実行時間、権限、トリガー、送信上限の制約があります。公式のApps Script quotasも確認してください。
次のコードは、フォーム送信をLeadsシートに追記し、金額とプランから優先度を決め、重要な問い合わせだけメール通知します。Apps Scriptエディタに貼り付け、フォーム送信のインストール型トリガーをonFormSubmitに設定します。
const SETTINGS = {
sheetName: "Leads",
notifyTo: "sales@example.com",
minAmountForHighPriority: 100000,
};
function onOpen() {
SpreadsheetApp.getUi()
.createMenu("Lead Ops")
.addItem("Rebuild lead status", "rebuildLeadStatus")
.addToUi();
}
function onFormSubmit(event) {
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet = spreadsheet.getSheetByName(SETTINGS.sheetName) || spreadsheet.insertSheet(SETTINGS.sheetName);
ensureHeader_(sheet);
const values = event && event.namedValues ? event.namedValues : {};
const company = first_(values, "Company");
const email = first_(values, "Email");
const plan = first_(values, "Plan");
const budget = Number(first_(values, "Budget") || 0);
const priority = classifyLead_(plan, budget);
sheet.appendRow([new Date(), company, email, plan, budget, priority, "new"]);
if (priority === "high") {
MailApp.sendEmail({
to: SETTINGS.notifyTo,
subject: `High priority lead: ${company}`,
body: `Company: ${company}\nEmail: ${email}\nPlan: ${plan}\nBudget: ${budget}`,
});
}
}
function rebuildLeadStatus() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SETTINGS.sheetName);
if (!sheet) throw new Error(`Sheet not found: ${SETTINGS.sheetName}`);
ensureHeader_(sheet);
const values = sheet.getDataRange().getValues();
for (let rowIndex = 1; rowIndex < values.length; rowIndex += 1) {
const row = values[rowIndex];
const plan = String(row[3] || "");
const budget = Number(row[4] || 0);
const priority = classifyLead_(plan, budget);
sheet.getRange(rowIndex + 1, 6).setValue(priority);
}
}
function ensureHeader_(sheet) {
const header = ["createdAt", "company", "email", "plan", "budget", "priority", "status"];
const current = sheet.getRange(1, 1, 1, header.length).getValues()[0];
if (current.join("") === "") {
sheet.getRange(1, 1, 1, header.length).setValues([header]);
sheet.setFrozenRows(1);
}
}
function classifyLead_(plan, budget) {
const normalizedPlan = String(plan).toLowerCase();
if (budget >= SETTINGS.minAmountForHighPriority || normalizedPlan.includes("team")) {
return "high";
}
if (budget >= 30000) {
return "medium";
}
return "low";
}
function first_(namedValues, key) {
const value = namedValues[key];
return Array.isArray(value) ? value[0] || "" : "";
}
この実例は、売上管理と問い合わせ管理の両方に使えます。フォームの項目名がCompanyではなく会社名なら、first_(values, "会社名")に変えます。Claude Codeに頼むときは、実際のフォーム項目名、通知条件、メール文面、個人情報をログに出さないことをセットで渡します。
Claude Codeへの依頼テンプレート
Claude Codeに「スプレッドシート連携を作って」と頼むだけでは範囲が広すぎます。次のテンプレートのように、入力、出力、触ってよいファイル、検証コマンド、守るべき情報を明示します。
You are working on spreadsheet automation for this repository.
Goal:
- Import sales CSV rows from data/sales.csv.
- Write a monthly summary to out/monthly-summary.csv.
- Add a Google Sheets append script for the Raw tab.
Scope:
- You may edit scripts/summarize-sales.mjs and scripts/append-lead-to-sheet.mjs.
- You may add small tests or sample CSV files if needed.
- Do not edit content files, product links, analytics, or deployment settings.
Rules:
- Do not commit credentials.
- Use environment variables for SHEET_ID and GOOGLE_APPLICATION_CREDENTIALS.
- Fail loudly on invalid dates, invalid amounts, and missing required columns.
- Keep the code copy-paste runnable with Node.js 20 or later.
Verification:
- Run node --check on every script you edit.
- Run the CSV summary against data/sales.csv.
- For Google Sheets API, verify syntax locally and list the manual credential checks.
- Return changed files, commands run, output summary, and remaining risks.
確認コマンドも固定しておきます。
node --check scripts/summarize-sales.mjs
node scripts/summarize-sales.mjs data/sales.csv out/monthly-summary.csv
node --check scripts/append-lead-to-sheet.mjs
git diff -- scripts/summarize-sales.mjs scripts/append-lead-to-sheet.mjs
チームで使うなら、このテンプレートをCLAUDE.mdやチケットの定型文に入れます。権限や許可コマンドの考え方はClaude Code permissions guideにもつながります。
実務ユースケース
1つ目は月次売上レポートです。Gumroad、Stripe、手動請求、無料PDF登録などをCSVで集め、wonだけを月次集計します。Claude Codeには「返金は除外」「0円リードは件数だけ数える」「商品名の表記ゆれを正規化する」というルールを渡します。Masaの運用では、最初から完璧なBIを作るより、まずCSV出力を固定したほうが早く回りました。
2つ目は問い合わせ管理です。フォーム回答をRawに追記し、予算、会社規模、相談内容から優先度を付けます。ここで大事なのは、AIに勝手な営業判断をさせないことです。teamを含むプラン、10万円以上の予算、既存顧客のメールドメインなど、人間が説明できる条件に限定します。
3つ目は記事と広告のKPI管理です。記事slug、公開日、検索流入、CTAクリック、商品クリック、相談フォーム到達をシートにまとめると、どの記事を改善すべきか見えます。関連してClaude Codeで分析実装を読むと、イベント名とCTAのそろえ方を決めやすくなります。
4つ目は請求前チェックです。請求CSVと納品ログを突き合わせ、金額が一致しない行だけを別シートに出します。ここは自動送信まで進めず、最初は「差分を人間に見せる」だけにするのが安全です。
落とし穴
最も多い失敗は、列名を固定しないことです。Amount、amount、金額が混ざると、スクリプトは動いても集計が欠けます。Claude Codeには必須列を列挙し、見つからない場合は終了する実装を求めます。
次に危ないのは、Google Sheetsをデータベースとして扱いすぎることです。Sheetsは共同編集と可視化には強いですが、トランザクション、排他制御、大量更新は得意ではありません。決済や権限の正本はDBに置き、Sheetsは確認用、運用用、レポート用に寄せます。
3つ目は認証情報の扱いです。サービスアカウントJSONをうっかり記事、Issue、チャット、Gitに貼ると、あとから消しても履歴に残ります。Claude Codeへの依頼文には「認証情報を読まない、出力しない、コミットしない」と書きます。
4つ目はApps Scriptのトリガー忘れです。コードを貼っただけではフォーム送信時に動かないことがあります。インストール型トリガー、実行ユーザー、メール送信権限、初回認可を公開前チェックに入れてください。
5つ目は、集計結果だけを見て満足することです。Rawの件数、除外件数、エラー件数、最終更新日時がないレポートは、いつ壊れたか分かりません。レポートには「何件読んで、何件除外し、何件集計したか」を残します。
CTA: 自動化を運用に変える
スプレッドシート自動化は、1本のスクリプトより運用ルールで差が出ます。まずは無料のClaude Codeチートシートで、依頼、差分確認、検証コマンドの型を手元に置いてください。繰り返し使うプロンプトやCLAUDE.mdを整えたい場合はClaudeCodeLabの教材・テンプレートが近道です。
チームで売上、問い合わせ、広告、請求のシートを扱うなら、権限、認証情報、レビュー、手動承認の線引きを先に決めたほうが安全です。その設計から一緒に作る場合はClaude Code研修・導入相談を使ってください。
この記事で紹介した内容を実際に試した結果
Masaの作業では、最初に効いたのはGoogle Sheets APIではなく、CSV集計をローカルで固定することでした。CSVの列名、除外ルール、エラー時の止まり方を決めると、Claude Codeへの依頼もレビューも急に具体的になります。Apps Scriptは問い合わせ分類には便利でしたが、通知条件を曖昧にするとメールが増えすぎました。結論として、スプレッドシート連携は「APIを書く作業」ではなく、「Raw、Clean、Reportの境界を決め、Claude Codeに検証まで任せる作業」として扱うのが一番安定しました。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
ObsidianメモをCLAUDE.mdに変えるClaude Code運用: 文脈を毎回説明しない仕組み
Obsidianの作業メモからCLAUDE.md用の運用ノートを作り、Claude Codeに安定した文脈を渡す方法。
Claude Code Revenue CTA Routing: 記事からPDF、Gumroad、相談へ送る設計
PVだけで終わらせず、読者の状態に合わせて無料PDF、Gumroad教材、導入相談へ分岐するCTA設計です。
Claude Codeチーム引き継ぎルール: レビュー、権限、収益導線まで渡す実務手順
Claude Codeの作業をチームで渡すための証拠、権限、ロールバック、無料PDF/Gumroad/相談導線の実務ルール。