GASで商品レビューをAIで分析し
改善点を自動抽出する方法
スプレッドシートに集めたレビューや口コミをClaude APIに渡し、不満点をテーマ別に分類しながら「具体的に何を直すべきか」を自動で抜き出す方法を、動くコードで解説します。
Table of Contents
感情分析との違いと、この記事のゴール
感情分析(レビューがポジティブかネガティブかを判定する処理)は「どれくらい満足されているか」を測る手法です。 一方この記事では、否定的なレビューから一歩踏み込み、「何を、どう改善すべきか」という具体的なアクションをAIに抜き出させます。人が数百件のレビューを読み込む代わりに、GASとClaude API(AnthropicのAIをHTTPで呼び出せるサービス)で自動化するのがゴールです。
テーマに分類
「配送・梱包」「品質」などあらかじめ決めたテーマに振り分ける
改善点を抽出
「梱包材を厚手に変更する」のように動詞で具体化する
根拠を引用
抽出のもとになったレビューの一節も返し、事実確認できるようにする
前提として、レビュー本文はスプレッドシートのA列に並んでいるものとします。 フォームやECモールのエクスポートから貼り付けるだけで使えます。
APIキーを安全に保管する
APIキー(AIを呼び出すための鍵)をコードに直接書くと、共有時に漏れる危険があります。 GASのPropertiesService(スクリプトごとの設定値を保存する仕組み)に保管し、コードからは名前で読み出します。
// スクリプトプロパティにAPIキーを保存しておく(コードに直書きしない)
// 「プロジェクトの設定」→「スクリプト プロパティ」で CLAUDE_API_KEY を登録
function getApiKey() {
const key = PropertiesService
.getScriptProperties()
.getProperty("CLAUDE_API_KEY");
if (!key) {
throw new Error("CLAUDE_API_KEY が未設定です。スクリプトプロパティに登録してください。");
}
return key;
}Claude APIを呼び出す共通関数
GASからHTTPリクエストを送るUrlFetchAppでClaudeのMessages APIを呼びます。muteHttpExceptions: trueを付けると、エラー時もレスポンス本文を取得できてデバッグしやすくなります。
// Claude API(Messages API)を呼び出す共通関数
// userText には分析用のプロンプト、systemText には役割・出力形式の指示を渡す
function callClaude(systemText, userText) {
const url = "https://api.anthropic.com/v1/messages";
const payload = {
model: "claude-sonnet-4-5",
max_tokens: 2000,
system: systemText,
messages: [{ role: "user", content: userText }],
};
const options = {
method: "post",
contentType: "application/json",
headers: {
"x-api-key": getApiKey(),
"anthropic-version": "2023-06-01",
},
payload: JSON.stringify(payload),
muteHttpExceptions: true,
};
const res = UrlFetchApp.fetch(url, options);
const code = res.getResponseCode();
if (code !== 200) {
throw new Error("Claude APIエラー: " + code + " / " + res.getContentText());
}
const body = JSON.parse(res.getContentText());
// Messages APIは content 配列の最初のテキストブロックに本文が入る
return body.content[0].text;
}モデル名(例:claude-sonnet-4-5)は用途とコストに応じて選びます。分類のような軽い処理なら、より軽量なモデルに変えるとコストを抑えられます。
改善点をJSONで受け取るプロンプト設計
AIの回答を機械的に扱うには、出力形式を固定するのが重要です。 テーマは選択肢から選ばせ、「本文にないことは推測しない」「JSON配列のみを返す」と明示します。 各レビューの番号(index)も返させると、あとで元の行に結果を紐づけられます。
// レビューをまとめて渡し、改善点をJSON配列で受け取るプロンプトを組み立てる
const THEMES = ["品質", "配送・梱包", "価格", "サイズ・仕様", "接客・対応", "その他"];
function buildPrompt(reviews) {
const list = reviews
.map((r, i) => (i + 1) + ". " + String(r).replace(/\n/g, " "))
.join("\n");
return [
"以下は当社商品のレビューです。各レビューから改善につながる不満点を抽出してください。",
"",
"# ルール",
"- テーマは次から必ず1つ選ぶ: " + THEMES.join(" / "),
"- レビュー本文に書かれていないことは推測しない",
"- 好意的なだけで不満がないレビューは結果に含めない",
"- 改善点は具体的な動詞で1文にまとめる(例: 梱包材を厚手に変更する)",
"",
"# 出力形式(JSON配列のみ。前後に説明文を付けない)",
'[{"index":1,"theme":"配送・梱包","point":"改善点","quote":"根拠となった一節"}]',
"",
"# レビュー",
list,
].join("\n");
}AIは前後に説明文を付けてくることがあるため、返答から[ ... ]の範囲だけを切り出して解析する関数も用意します。
// AIの返答からJSON部分だけを安全に取り出す
// 前後に説明文が混ざっても [ ... ] の範囲を切り出して解析する
function parseJsonArray(text) {
const start = text.indexOf("[");
const end = text.lastIndexOf("]");
if (start === -1 || end === -1) {
throw new Error("JSON配列が見つかりませんでした: " + text);
}
return JSON.parse(text.substring(start, end + 1));
}10件ずつまとめて分析するバッチ処理
1件ずつAPIを呼ぶと通信回数が増え、GASの6分実行時間制限に達しやすくなります。 10件程度をまとめて1回のリクエストで送り、処理済みの行はフラグ列でスキップします。
// レビュー列を10件ずつ分析し、改善点シートに書き出す
function analyzeReviews() {
const ss = SpreadsheetApp.getActive();
const src = ss.getSheetByName("レビュー"); // A:レビュー本文, B:処理済みフラグ
const out = ss.getSheetByName("改善点"); // 抽出結果の出力先
const values = src.getDataRange().getValues();
const system = "あなたはECサイトの品質改善担当です。指定されたJSON形式のみで回答します。";
const BATCH = 10;
const buffer = []; // {rowNumber, text}
const results = [];
for (let i = 1; i < values.length; i++) { // 1行目はヘッダー
const text = values[i][0];
const done = values[i][1];
if (!text || done) continue; // 空行・処理済みはスキップ
buffer.push({ rowNumber: i + 1, text: text });
if (buffer.length === BATCH || i === values.length - 1) {
flushBatch(buffer, system, src, out, results);
buffer.length = 0;
Utilities.sleep(1000); // レート制限に配慮して間隔を空ける
}
}
Logger.log(results.length + "件の改善点を抽出しました");
}実際にAPIへ送って結果を書き込む部分は、リトライ(失敗時の再試行)を挟んで安定させます。 一時的な通信エラーやレート制限(短時間の呼び出し回数の上限)に当たっても、待ってから再実行します。
// 1バッチ分をAPIに送り、結果を書き込む(リトライ付き)
function flushBatch(buffer, system, src, out, results) {
const reviews = buffer.map((b) => b.text);
const prompt = buildPrompt(reviews);
let items;
for (let attempt = 1; attempt <= 3; attempt++) {
try {
items = parseJsonArray(callClaude(system, prompt));
break;
} catch (e) {
if (attempt === 3) throw e;
Utilities.sleep(attempt * 2000); // 2s→4sと待って再試行
}
}
items.forEach((item) => {
const src2 = buffer[item.index - 1]; // index は1始まり
const rowNo = src2 ? src2.rowNumber : "";
out.appendRow([new Date(), rowNo, item.theme, item.point, item.quote]);
results.push(item);
});
// 送った行を処理済みにする(B列に true)
buffer.forEach((b) => src.getRange(b.rowNumber, 2).setValue(true));
}テーマ別に集計して優先順位をつける
抽出した改善点をテーマごとに数えれば、どの領域に不満が集中しているかが分かります。 件数の多いテーマから対策すれば、限られたリソースで効果の大きい改善に集中できます。
// 改善点シートをテーマ別に集計し、多い順に並べる
function summarizeByTheme() {
const out = SpreadsheetApp.getActive().getSheetByName("改善点");
const rows = out.getDataRange().getValues().slice(1); // ヘッダー除外
const counts = {};
rows.forEach((row) => {
const theme = row[2]; // C列=テーマ
if (!theme) return;
counts[theme] = (counts[theme] || 0) + 1;
});
const ranking = Object.keys(counts)
.map((theme) => ({ theme: theme, count: counts[theme] }))
.sort((a, b) => b.count - a.count);
ranking.forEach((r) => Logger.log(r.theme + ": " + r.count + "件"));
return ranking;
}トリガーで定期実行を自動化する
新しいレビューが継続的に増えるなら、時間主導トリガー(決まった時刻に自動実行する仕組み)で定期分析します。 未処理分だけを対象にしているため、毎週実行しても同じレビューを二重に分析しません。
// 毎週月曜の朝に自動でレビュー分析を実行する
function createWeeklyTrigger() {
ScriptApp.newTrigger("analyzeReviews")
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(7)
.create();
}運用時の注意点
AIの抽出はあくまで下書きです。改善点シートに引用を残しているので、施策化する前に担当者が根拠を確認しましょう。 また件数が非常に多い場合は、1回のトリガーで処理する上限件数を決め、時間主導トリガーを短い間隔で複数回動かすと実行時間制限を回避できます。
まとめ
GASとClaude APIを組み合わせれば、大量のレビューから改善点をテーマ別に自動抽出できます。 ポイントは、テーマを選択肢で固定し、JSONで受け取り、引用を残して事実確認できるようにすることです。 バッチ処理と定期トリガーを加えれば、レビューを「集めるだけ」から「改善に活かす」仕組みへと変えられます。
よくある質問
感情分析はレビューが好意的か否定的かをスコア化するのが目的です。この記事の方法は一歩進めて、否定的なレビューから「何を、どう改善すべきか」という具体的なアクションを抜き出します。たとえば『梱包が雑で角が潰れていた』というレビューから、テーマ=配送・改善点=梱包材の見直し、という形で構造化して受け取ります。
できます。1件ずつAPIを呼ぶと実行時間制限に達しやすいため、10件程度をまとめて1回のリクエストで送るバッチ処理にします。それでも件数が多い場合は、処理済みフラグ列で未処理分だけを対象にし、時間主導トリガーで数回に分けて実行すると6分の実行時間制限を回避できます。
プロンプトで「レビュー本文に書かれていない内容は推測しない」「該当する不満がなければ空配列を返す」と明示するのが有効です。さらに改善点だけでなく根拠となったレビューの一節(引用)も一緒に返させると、担当者が事実確認しやすくなり、誤った抽出に気づけます。
自社の商材に合わせて「品質」「配送・梱包」「価格」「サイズ・仕様」「接客・対応」などを事前にプロンプトへ列挙し、その中から選ばせるのが安定します。自由に分類させると表記がぶれて集計しづらくなるため、選択肢を固定するのがポイントです。
テーマ別に件数を集計すると、どの領域の不満が多いかがひと目で分かります。件数が多いテーマから優先的に対策を検討し、月次でSlackやメールにレポートを自動送信すれば、改善サイクルを仕組み化できます。この記事ではテーマ別集計までのコードを紹介します。
AI×GASで
業務を自動化する。
レビュー分析のほかにも、問い合わせ分類・要約・書類読み取りなど、Claude APIとGASを組み合わせた業務自動化をご相談いただけます。