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分)

  1. OpenAIにログインし、APIの管理画面を開きます。
  2. 左メニューの「API keys」→「Create new secret key」から新規キーを作成。表示されたキー(例:sk-で始まる文字列)をメモに安全に保管。
  3. 「Billing」で支払い方法にクレジットカードを登録します。無料トライアルがない時期は、この登録がないとAPIが動きません。

APIは機械同士の会話口でキーは身分証のようなものなので、コピーしたら他人に見えない場所に保管してください。

本記事のサンプルは軽量モデル(gpt-4o-mini)を使います。数十行のテストなら数円〜数十円で収まることが多いです(実行量に依存)。

4-3. Apps Script(GAS)の準備(5分)

  1. スプレッドシート上で「拡張機能」→「Apps Script」を開きます。
  2. プロジェクト名を「ChatGPT_自動生成」などに変更。
  3. コードエディタに、下記コードを丸ごと貼り付けて保存(ディスクアイコン)。
// ========= 設定ここから =========
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分)

  1. Apps Scriptの画面右上「歯車」→「プロジェクトのプロパティ」を開く。
  2. 「スクリプトのプロパティ」タブで「新しいプロパティを追加」。
  3. プロパティ名: OPENAI_API_KEY、値: 取得したAPIキー(sk-で始まる)を貼り付けて保存。

コードに直接キーを書かず、プロパティ保管にすると漏えいリスクを減らせます。

4-5. 初回実行で承認&トリガー作成(2分)

  1. エディタ上部の関数プルダウンから「setup」を選び、実行ボタンを押す。
  2. Googleの承認ダイアログが出るので、内容を確認して許可。
  3. 完了アラートが出たら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キーは個人情報と同等に扱い、社外共有のシートやコードに直接書かないようにしましょう。

ここまで到達できたら、もう立派な「自動化デビュー」です。お疲れさまでした!