ChatGPTでスプレッドシートの文章を自動生成する方法【30分で完成】
この記事は、初めての自動化でも迷わず「最後まで作り切る」ことを最優先に書いています。むずかしい仕組みは最小限でいっしょに手を動かし、30分で「スプレッドシートに書いたお題から、ChatGPTが自動で文章を作ってくれる」ところまで作ってみましょう。
1. この記事でできること
ゴールはシンプルです。スプレッドシートのA列に「題材(例:商品名、記事テーマ)」、B列に「トーン(例:やさしい、営業向け)」を入力すると、数秒後にC列へChatGPTが自動で短い文章(150〜200字の紹介文など)を生成・追記します。
一度セットすれば、以降は入力するだけで自動生成されるので、繰り返し作業が一気にラクになります。無料の範囲で進めますが、ChatGPTのAPI利用は2026年時点でクレジットカード登録が必要です(後述)。
2. 今回作る自動化の仕組みの全体像
全体像は次の流れです。
- スプレッドシートに題材とトーンを入力
- 編集を検知するトリガー(スイッチ)が作動
- Google Apps Script(GAS)がChatGPT APIに質問
- 返ってきた文章をC列に自動書き込み
トリガーを入れる理由は「手動でボタンを押さなくても、入力をきっかけに自動で動く」状態にするためです。まずは「動くもの」を最短で作りましょう。
3. 事前に必要なアカウント・準備物
| 項目 | 用途 | 登録・取得するもの | 料金/必要情報 |
|---|---|---|---|
| Googleアカウント | スプレッドシートとGASの利用 | Googleアカウントのログイン | 無料 |
| スプレッドシート | 題材の管理と出力の保存 | 新規スプレッドシート | 無料(Google Drive内) |
| OpenAI(ChatGPT)アカウント | 文章生成(API経由) | APIキーの発行 | API利用はクレカ登録が必要。少額から利用可 |
補足:ChatGPTの「Webチャット画面」だけでは自動化できません。今回はAPI(アプリ連携用の窓口)を使うため、OpenAIのAPIキーが必要になります。
初めてだと勘違いしやすいポイントなので注意してくださいね。
4. 必要なツール、アカウントの設定
4-1. スプレッドシートの雛形を作る(3分)
Google Driveで新規スプレッドシートを作成し、シート名を「AI生成」などに変更します。1行目に次の見出しを入れてください。
- A1: 題材
- B1: トーン
- C1: 出力(自動)
最低限の入力(題材+トーン)だけで作ってみて出力先を固定することで、後の自動処理が分かりやすくなります。1行目はメニュー表示のため「表示」→「固定」→「1行」を選ぶと作業しやすくなります。
4-2. OpenAI(ChatGPT)APIキーを取得(5〜10分)
- OpenAIにログインし、APIの管理画面を開きます。
- 左メニューの「API keys」→「Create new secret key」から新規キーを作成。表示されたキー(例:sk-で始まる文字列)をメモに安全に保管。
- 「Billing」で支払い方法にクレジットカードを登録します。無料トライアルがない時期は、この登録がないとAPIが動きません。
APIは機械同士の会話口でキーは身分証のようなものなので、コピーしたら他人に見えない場所に保管してください。
本記事のサンプルは軽量モデル(gpt-4o-mini)を使います。数十行のテストなら数円〜数十円で収まることが多いです(実行量に依存)。
4-3. Apps Script(GAS)の準備(5分)
- スプレッドシート上で「拡張機能」→「Apps Script」を開きます。
- プロジェクト名を「ChatGPT_自動生成」などに変更。
- コードエディタに、下記コードを丸ごと貼り付けて保存(ディスクアイコン)。
// ========= 設定ここから =========
const MODEL_NAME = 'gpt-4o-mini'; // 低コストで十分高品質
const OUTPUT_CHAR_HINT = '150〜200字'; // 出力の目安
// ========= 設定ここまで =========
/** 初期設定:既存トリガー削除→onEditトリガー作成 */
function setup() {
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach(t => ScriptApp.deleteTrigger(t));
ScriptApp.newTrigger('handleEdit')
.forSpreadsheet(SpreadsheetApp.getActive())
.onEdit()
.create();
SpreadsheetApp.getUi().alert('初期設定が完了しました。シートを編集すると自動生成されます。');
}
/** メニュー追加 */
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('AI生成')
.addItem('初期設定(トリガー作成)', 'setup')
.addItem('選択行を手動生成', 'generateSelected')
.addToUi();
}
/** 編集トリガー本体(インストール型) */
function handleEdit(e) {
try {
const range = e && e.range;
if (!range) return; // 念のため
const sheet = range.getSheet();
const row = range.getRow();
const col = range.getColumn();
if (row === 1) return; // 見出し行は無視
if (col > 2) return; // A/B列の変更だけ反応
generateRow_(sheet, row);
} catch (err) {
console.error(err);
}
}
/** 選択行をまとめて生成(手動実行用) */
function generateSelected() {
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getActiveRange();
const start = range.getRow();
const end = start + range.getNumRows() - 1;
for (let r = start; r <= end; r++) {
if (r === 1) continue; // 見出しスキップ
generateRow_(sheet, r);
}
}
/** 指定行を生成 */
function generateRow_(sheet, row) {
const lock = LockService.getDocumentLock();
lock.waitLock(20 * 1000);
try {
const topic = sheet.getRange(row, 1).getValue(); // A:題材
const tone = sheet.getRange(row, 2).getValue() || 'やさしい'; // B:トーン
if (!topic) return; // 題材が空なら何もしない
// すでに出力済みなら上書きしない(必要なら消して再入力)
const outCell = sheet.getRange(row, 3);
const existing = (outCell.getValue() + '').trim();
if (existing) return;
outCell.setValue('生成中…');
const prompt = buildPrompt_(topic, tone);
const result = callChatGPT_(prompt);
outCell.setValue(result);
} catch (err) {
sheet.getRange(row, 3).setValue('エラー: ' + err.message);
console.error(err);
} finally {
lock.releaseLock();
}
}
/** プロンプト(指示文)を作る */
function buildPrompt_(topic, tone) {
return [
'あなたは日本語のライティングアシスタントです。',
`次の題材について${OUTPUT_CHAR_HINT}の紹介文を作成してください。`,
'要件:',
'- 結論を先に書く',
'- 箇条書きは使わない',
'- 固有名詞の誤りに注意',
`- トーン: ${tone}`,
`題材: ${topic}`
].join('\n');
}
/** OpenAI Chat Completions API を呼び出す */
function callChatGPT_(userPrompt) {
const apiKey = PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY');
if (!apiKey) throw new Error('APIキー未設定(プロジェクトのプロパティに OPENAI_API_KEY を保存)');
const url = 'https://api.openai.com/v1/chat/completions';
const payload = {
model: MODEL_NAME,
messages: [
{ role: 'system', content: 'あなたは正確で簡潔な日本語ライターです。' },
{ role: 'user', content: userPrompt }
],
temperature: 0.7,
max_tokens: 600
};
const res = UrlFetchApp.fetch(url, {
method: 'post',
contentType: 'application/json',
headers: { Authorization: 'Bearer ' + apiKey },
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
const code = res.getResponseCode();
const text = res.getContentText();
if (code >= 300) {
throw new Error('OpenAIエラー ' + code + ': ' + text);
}
const data = JSON.parse(text);
return data.choices[0].message.content.trim();
}
編集(onEdit)を合図に、A/B列の内容からプロンプトを組み立て、ChatGPT APIを叩いてC列へ書き戻すためにこのコードが必要です。ロックやエラーハンドリングも最初から入れておくと、複数行を続けて編集した際の取りこぼしを減らせます。
4-4. APIキーをGASに安全に保存(2分)
- Apps Scriptの画面右上「歯車」→「プロジェクトのプロパティ」を開く。
- 「スクリプトのプロパティ」タブで「新しいプロパティを追加」。
- プロパティ名: OPENAI_API_KEY、値: 取得したAPIキー(sk-で始まる)を貼り付けて保存。
コードに直接キーを書かず、プロパティ保管にすると漏えいリスクを減らせます。
4-5. 初回実行で承認&トリガー作成(2分)
- エディタ上部の関数プルダウンから「setup」を選び、実行ボタンを押す。
- Googleの承認ダイアログが出るので、内容を確認して許可。
- 完了アラートが出たらOK。これで「インストール型のonEditトリガー」が作られました。
ここで失敗しても大丈夫。承認に失敗したら、もう一度「setup」を実行してください。承認は最初の一回だけ必要です。
5. 自動化ツールの設定
この章でやることは2つだけです。
- シートにテスト用の行を入れる
- メニュー「AI生成」→「選択行を手動生成」で動作チェック(万一のため)
まず、シートに次のように入力してください(2〜3行でOK)。
- A2: 無印良品の方眼ノート / B2: ていねい
- A3: 社内報の巻頭あいさつ / B3: フレンドリー
入力後、数秒でC列に文章が入りはじめれば成功です。もし無反応なら、メニュー「AI生成」→「選択行を手動生成」を押してみてください。これは強制的に生成を走らせるための“手動テストスイッチ”です。ここで動けば、トリガー設定の問題に切り分けられます。
6. 動作確認(実際の質問例)
標準のプロンプトは「紹介文」向けですが、題材とトーンを工夫するといろいろ使えます。いくつか例を試しましょう。
- 例1)A: 新作ハンドドリップコーヒーの紹介 / B: 温かみのある
- 例2)A: 月次レポートのメール本文(要件: 指標と次アクションを先に) / B: 端的に
- 例3)A: ECサイトの返品案内(優しい印象に) / B: ていねい
ポイント:B列は“雰囲気を一言で伝える”欄です。長文の指示をA列に入れてもOKですが、まずは短い言葉で試すと安定します。失敗しても大丈夫。C列の「生成中…」が長く続く場合は、いったんEscで編集を止め、C列を空にしてもう一度A/B列を編集してみましょう。
7. うまく動かないときのチェックポイント
- APIキー未設定エラー:C列に「APIキー未設定」と出たら、プロジェクトのプロパティにOPENAI_API_KEYが入っているか再確認。
- 401 Unauthorized:APIキーが間違っているか、無効。キーを再発行し保存し直す。
- 429 Too Many Requests:短時間に実行しすぎ。少し待って再実行。頻繁に起こる場合は行ごとの編集間隔を空けるか、手動生成に切替。
- トリガーが動かない:Apps Scriptの「トリガー」画面で「handleEdit」のonEditトリガーがあるか確認。なければ「setup」を再実行。
- 権限が不足:初回承認をスキップすると外部通信がブロックされます。もう一度「setup」を実行して承認。
- モデル名エラー:MODEL_NAMEが実在しないとエラー。例のまま gpt-4o-mini を使うのが安全。
- 出力が空/短すぎる:max_tokensが小さすぎると途中で切れます。コード内のmax_tokens: 600をそのまま使うか、やや増やす。
- 会社PCの制限:外部API通信が制限されている環境では失敗することがあります。自宅回線か別ブラウザで再テスト。
- タイムアウト:長文を大量行で一気に生成すると時間超過の可能性。まずは数行ずつ実行。
ログの見方:Apps Scriptの「実行ログ」を開くと詳細が見られます。エラー文はそのまま検索すると原因に当たりやすいです。
8. 次にできる改善アイデア
- テンプレ列の追加:D列に「出力種別(例:紹介文/要約/メール)」を置き、buildPrompt_で分岐。ワンクリックで用途を切替。
- 原稿の長さを可変に:E列に「文字数目安」を入れ、OUTPUT_CHAR_HINTの代わりに参照。
- コスト管理:1日あたりの生成回数を制限する簡易カウンタをPropertiesに保存してチェック。上限超過時は停止メッセージを返す。
- 品質の安定化:systemメッセージに「禁止事項(個人情報・医療助言など)」を明示して、社内利用時のリスクを軽減。
- 一括バッチ生成:時間主導トリガー(毎時)で未生成行をまとめて処理。人の編集を待たず夜間に自動生成できます。
- 代替モデルの比較:より安価/高速なモデルや他社モデル(例:OpenAIの別バリアント)を切替できるようMODEL_NAMEをシートから読込。
大事なのは「まずは動かす」→「使いどころを増やす」の順番です。今日つくった最小構成が、そのままチームの共通下書き機に育っていきます。
費用と安全面の補足:
- 費用はリクエスト量に比例します。まずは少行で検証し、上限(1日何行など)を決めると安心です。
- APIキーは個人情報と同等に扱い、社外共有のシートやコードに直接書かないようにしましょう。
ここまで到達できたら、もう立派な「自動化デビュー」です。お疲れさまでした!