GAS×ChatGPTバッチ処理|大量データを一括処理する方法
スプレッドシートに100件のデータがある。1件ずつChatGPTに入力していたら、それだけで半日が潰れる。
GASとChatGPT APIを組み合わせたバッチ処理なら、100件のデータもボタン1つで一括処理できる。API制限対策やコスト最適化も含めた実装方法を、コピペで動くコード付きで解説する。
目次
バッチ処理とは
バッチ処理の定義
バッチ処理とは、複数のデータをまとめて一括で処理する方式のことだ。
| 処理方式 | 説明 | 例 |
|---|---|---|
| リアルタイム処理 | 1件ずつ即座に処理 | Webフォーム送信 |
| バッチ処理 | 複数件をまとめて処理 | 月末の給与計算 |
GASとChatGPTの組み合わせでは、スプレッドシートの複数行を順番にAPIへ送信し、結果を一括で書き戻す。これがバッチ処理にあたる。
なぜバッチ処理が必要か
1. 手作業の限界
| 件数 | 手作業(1件2分) | バッチ処理 |
|---|---|---|
| 10件 | 20分 | 30秒 |
| 100件 | 3時間20分 | 3分 |
| 1,000件 | 33時間 | 30分 |
2. API制限への対処
ChatGPT APIにはレート制限がある。
| プラン | RPM(リクエスト/分) | TPM(トークン/分) |
|---|---|---|
| 無料 | 3 | 40,000 |
| Tier 1 | 60 | 60,000 |
| Tier 2以上 | 5,000+ | 800,000+ |
バッチ処理で適切な待機時間を挟めば、レート制限を回避しながら安定して処理を進められる。
3. コスト最適化
一括処理すると、システムプロンプトの重複送信を減らせる。
| 処理方式 | システムプロンプト | コスト効率 |
|---|---|---|
| 1件ずつ(100回) | 100回送信 | 低 |
| バッチ処理 | 1回 + ループ | 高 |
実装手順
事前準備
以下が必要になる。
- Googleアカウント
- OpenAI APIキー(取得方法)
- スプレッドシート(処理対象データ)
STEP 1: スプレッドシートを準備
| A列 | B列 | C列 |
|---|---|---|
| 入力データ | ステータス | 出力結果 |
| テキスト1 | ||
| テキスト2 |
1行目はヘッダー行として使う。
STEP 2: GASスクリプトを作成
「拡張機能」→「Apps Script」で以下のコードを貼り付ける。
/**
* GAS×ChatGPT バッチ処理スクリプト
*
* 機能:
* - スプレッドシートのデータを一括でChatGPT APIに送信
* - レート制限対策(待機時間)
* - エラーハンドリング
* - 進捗表示
*/
// ============================================
// 設定
// ============================================
const CONFIG = {
SHEET_NAME: 'シート1', // シート名
INPUT_COL: 1, // 入力列(A列=1)
STATUS_COL: 2, // ステータス列(B列=2)
OUTPUT_COL: 3, // 出力列(C列=3)
START_ROW: 2, // 開始行(ヘッダー除く)
WAIT_MS: 1500, // APIコール間の待機時間(ミリ秒)
MODEL: 'gpt-4o-mini', // 使用モデル
MAX_TOKENS: 500, // 最大トークン数
};
// ============================================
// メイン処理
// ============================================
function processBatch() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(CONFIG.SHEET_NAME);
const lastRow = sheet.getLastRow();
const apiKey = PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY');
if (!apiKey) {
SpreadsheetApp.getUi().alert('APIキーが設定されていません。\nスクリプトプロパティに OPENAI_API_KEY を設定してください。');
return;
}
let processedCount = 0;
let errorCount = 0;
for (let row = CONFIG.START_ROW; row <= lastRow; row++) {
const inputValue = sheet.getRange(row, CONFIG.INPUT_COL).getValue();
const status = sheet.getRange(row, CONFIG.STATUS_COL).getValue();
// 空行または処理済みはスキップ
if (!inputValue || status === '完了') {
continue;
}
// 処理中ステータスを設定
sheet.getRange(row, CONFIG.STATUS_COL).setValue('処理中...');
SpreadsheetApp.flush();
try {
// ChatGPT APIを呼び出し
const result = callChatGPT(apiKey, inputValue);
// 結果を書き込み
sheet.getRange(row, CONFIG.OUTPUT_COL).setValue(result);
sheet.getRange(row, CONFIG.STATUS_COL).setValue('完了');
processedCount++;
} catch (error) {
// エラー処理
sheet.getRange(row, CONFIG.STATUS_COL).setValue('エラー: ' + error.message);
errorCount++;
console.error(`Row ${row}: ${error.message}`);
}
// レート制限対策: 待機
if (row < lastRow) {
Utilities.sleep(CONFIG.WAIT_MS);
}
}
// 完了通知
SpreadsheetApp.getUi().alert(
`処理完了\n\n` +
`成功: ${processedCount}件\n` +
`エラー: ${errorCount}件`
);
}
// ============================================
// ChatGPT API呼び出し
// ============================================
function callChatGPT(apiKey, inputText) {
const url = 'https://api.openai.com/v1/chat/completions';
// プロンプトをカスタマイズ
const systemPrompt = `あなたは優秀なアシスタントです。ユーザーの入力に対して簡潔に回答してください。`;
const payload = {
model: CONFIG.MODEL,
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: inputText }
],
max_tokens: CONFIG.MAX_TOKENS,
temperature: 0.7
};
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': 'Bearer ' + apiKey
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
const responseCode = response.getResponseCode();
const responseText = response.getContentText();
if (responseCode !== 200) {
const errorData = JSON.parse(responseText);
throw new Error(errorData.error?.message || `HTTP ${responseCode}`);
}
const data = JSON.parse(responseText);
return data.choices[0].message.content.trim();
}
// ============================================
// メニュー追加
// ============================================
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('ChatGPT一括処理')
.addItem('バッチ処理を実行', 'processBatch')
.addItem('APIキーを設定', 'setApiKey')
.addToUi();
}
// ============================================
// APIキー設定ダイアログ
// ============================================
function setApiKey() {
const ui = SpreadsheetApp.getUi();
const result = ui.prompt(
'APIキー設定',
'OpenAI APIキーを入力してください:',
ui.ButtonSet.OK_CANCEL
);
if (result.getSelectedButton() === ui.Button.OK) {
const apiKey = result.getResponseText().trim();
if (apiKey) {
PropertiesService.getScriptProperties().setProperty('OPENAI_API_KEY', apiKey);
ui.alert('APIキーを保存しました。');
}
}
}
STEP 3: APIキーを設定
- メニュー「ChatGPT一括処理」→「APIキーを設定」
- OpenAI APIキーを入力
- 「OK」をクリック
STEP 4: 実行
- A列にデータを入力
- メニュー「ChatGPT一括処理」→「バッチ処理を実行」
- 完了を待つ
処理時間・コスト比較表
処理時間の目安
| 件数 | 待機時間1.5秒 | 待機時間1秒 | 待機なし |
|---|---|---|---|
| 10件 | 20秒 | 15秒 | 5秒 |
| 50件 | 1分30秒 | 1分 | 25秒 |
| 100件 | 3分 | 2分 | 50秒 |
| 500件 | 15分 | 10分 | 4分 |
※API応答時間を約0.5秒として計算
APIコストの目安(gpt-4o-mini)
| 件数 | 入力(平均500トークン) | 出力(平均200トークン) | 合計コスト |
|---|---|---|---|
| 10件 | $0.00075 | $0.0006 | 約$0.001 |
| 100件 | $0.0075 | $0.006 | 約$0.01 |
| 1,000件 | $0.075 | $0.06 | 約$0.14 |
gpt-4o-mini料金(2026年1月時点):
- 入力: $0.15 / 100万トークン
- 出力: $0.60 / 100万トークン
注意点と対策
1. レート制限(Rate Limit)
問題: APIを短時間に呼びすぎると制限がかかる。
対策:
// 待機時間を設定(推奨: 1〜2秒)
Utilities.sleep(1500);
エラー例と対処:
// 429エラー(Too Many Requests)の場合、リトライ
if (responseCode === 429) {
Utilities.sleep(60000); // 1分待機
// リトライ処理
}
2. 実行時間制限(6分)
問題: GASの実行時間は最大6分に制限されている。
対策: 大量データは分割して処理する。
function processBatchWithContinuation() {
const props = PropertiesService.getScriptProperties();
let startRow = parseInt(props.getProperty('BATCH_START_ROW')) || 2;
const BATCH_SIZE = 50; // 1回の処理件数
// 50件ずつ処理
const endRow = Math.min(startRow + BATCH_SIZE - 1, lastRow);
for (let row = startRow; row <= endRow; row++) {
// 処理
}
if (endRow < lastRow) {
// 次のバッチのために開始行を保存
props.setProperty('BATCH_START_ROW', (endRow + 1).toString());
// 1分後に再実行
ScriptApp.newTrigger('processBatchWithContinuation')
.timeBased()
.after(60000)
.create();
} else {
// 完了
props.deleteProperty('BATCH_START_ROW');
}
}
3. エラーハンドリング
リトライ付きの呼び出し例:
function callChatGPTWithRetry(apiKey, inputText, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return callChatGPT(apiKey, inputText);
} catch (error) {
lastError = error;
console.log(`Attempt ${attempt} failed: ${error.message}`);
if (attempt < maxRetries) {
// 指数バックオフで待機
const waitTime = Math.pow(2, attempt) * 1000;
Utilities.sleep(waitTime);
}
}
}
throw lastError;
}
4. コスト管理
使用量の監視:
// トークン数をログに記録
function logUsage(response) {
const usage = response.usage;
console.log(`Tokens - Input: ${usage.prompt_tokens}, Output: ${usage.completion_tokens}`);
}
OpenAIダッシュボードで月額上限を設定しておくと安心だ。
応用例
商品説明文の自動生成
const systemPrompt = `
あなたはECサイトのコピーライターです。
商品の特徴から、購買意欲を高める説明文を150字程度で作成してください。
`;
アンケート回答の分類
const systemPrompt = `
以下のアンケート回答を分類してください。
分類: ポジティブ / ネガティブ / 中立
形式: 分類名のみを回答
`;
メール文面の自動作成
const systemPrompt = `
以下の情報から、ビジネスメールの本文を作成してください。
丁寧で簡潔な文章にしてください。
`;
まとめ
バッチ処理のポイント
| ポイント | 内容 |
|---|---|
| 待機時間 | 1〜2秒の間隔を空ける |
| エラー処理 | try-catchでエラーをキャッチ |
| 進捗表示 | ステータス列で進捗を可視化 |
| 分割処理 | 100件以上は分割実行 |
このスクリプトでできること
- スプレッドシートの大量データを一括処理
- API制限を回避しながら安定実行
- エラーが発生しても他の行は継続処理
- 処理状況をリアルタイムで確認
次のステップ
- GAS×ChatGPT入門|基本的な連携方法[リンク予定]
- GAS×ChatGPTで自動返信Botを作ろう[リンク予定]
- ChatGPT API料金ガイド[リンク予定]
コメント