Googleフォーム×GASで回答を自動通知・集計する方法|コピペで動く3レシピ
「フォームに回答が来ても、誰も気づかない」「定例会議の前に毎回スプレッドシートを集計し直すのが地味に面倒」——Googleフォームを業務で使い始めると、ほとんどの人が一度はぶつかる悩みです。
この記事では、Googleフォームの回答をスプレッドシートとGAS(Google Apps Script)で連携させて、Gmail通知・Slack通知・集計シート自動更新の3つを「コピペで動く形」で実装する方法を解説します。GASは入門レベル前提で、1本ずつ動かしながら読めるよう構成しました。
GASのスクリプトエディタの開き方や保存・実行の基本操作は、シリーズ1本目で詳しく解説しています。GASに初めて触る方は先にこちらから読むとつまずきにくいです。
なお、LINE Notifyは2025年3月末でサービス終了しているため、本記事では現行で誰でも使える「Slack Incoming Webhook」と「Gmail」を通知手段として採用しています。古い記事のコードでつまずいた経験がある方も、ここから読み直せば動くところまで戻れます。
GoogleフォームとGASを連携してできること
Googleフォームは標準でも「回答をスプレッドシートに集める」機能を持っていますが、それだけだと「シートに溜まる」止まりで、能動的に何かが起きるわけではありません。GASを噛ませることで、はじめて以下のような自動化が可能になります。
自動通知(Slack・Gmail・チャット)
回答が送信された瞬間に、指定の宛先へ通知を飛ばせます。本記事で扱うのはGmailとSlackですが、同じ仕組みでDiscordやMicrosoft Teamsにも応用できます(DiscordはWebhookの仕様がSlackとほぼ同じ)。
- 問い合わせフォーム → 担当者にGmail通知
- 申込フォーム → 営業チームのSlackチャンネルに通知
- イベント参加登録 → 運営チャンネルに通知
「気づかない」を解消するだけで、対応スピードが半日単位で改善することも珍しくありません。
自動集計(別シートでのカテゴリ別カウント)
回答が来るたびに、別シートに置いた集計表のカウントを1ずつ加算する処理を自動化できます。会議前の手動集計から解放され、いつ見ても最新の数字が乗っている状態を作れます。
スプレッドシート関数のCOUNTIFでも同じことはできますが、GASを使う利点は「集計時に他の処理(通知・条件分岐)を一緒にやれる」点です。レシピ3で詳しく扱います。
関連処理の自動化(タスク登録・差し込み返信)
応用範囲はもっと広く、たとえば以下のような連携もGASなら実装できます。
- 回答内容に応じて自動返信メールを送る(フォーム標準の返信機能より柔軟)
- 特定キーワードを含む回答だけを別シートに転記する
- Googleカレンダーに予定を自動登録する
本記事では3つの基本レシピに絞りますが、土台が同じなので、ここで紹介するレシピさえ動けば応用は難しくありません。
連携の前提|フォーム回答スプレッドシートを準備する
GASでフォーム連携を始める前に、「回答先のスプレッドシート」を準備しておきます。Googleフォーム単体ではなく、フォームの回答が流れ込むスプレッドシートが起点になる点が重要です。
フォーム回答先のスプレッドシートを開く
- Googleフォームの編集画面を開く
- 上部タブ「回答」をクリック
- 右側の緑色の「スプレッドシートにリンク」アイコンをクリック
- 「新しいスプレッドシートを作成」または「既存のスプレッドシートを選択」
これで、フォームに回答が届くたびに、指定したスプレッドシートに行が追加されるようになります。
| タイムスタンプ | お名前 | カテゴリ | 内容 |
|---|---|---|---|
| 2026/05/09 10:23:11 | 田中 | 質問 | 営業時間を教えてください |
| 2026/05/09 10:45:02 | 佐藤 | 申込 | サービスAを申し込みたい |
1行目がヘッダー(質問のテキスト)、2行目以降が回答です。GASからはこの「質問テキスト」をキーに回答内容を取り出します。後でコードを書くときに重要になるので、質問のテキストはあらかじめ確定させておくと安全です。
拡張機能 → Apps Script を開く
回答先スプレッドシートを開いた状態で、メニューから次の手順でスクリプトエディタを開きます。
- スプレッドシート上部メニュー「拡張機能」をクリック
- 「Apps Script」を選択
- 別タブでエディタが開く
ここから先は基本的に1本目記事の延長です。エディタの基本操作(保存・実行・ログ表示)に不安がある方は、こちらを先に押さえておきましょう。
レシピ1|回答が来たらGmailに自動通知する(GAS)
最も簡単で、追加の設定が要らないレシピです。GmailApp はGASにビルトインされている上に、外部のWebhook URLを取得する手間もありません。まずはこのレシピで「フォーム送信トリガーが発火する感覚」を掴んでから、レシピ2に進むのがおすすめです。
コピペで動くサンプルコード
スクリプトエディタに貼り付けて、'your-address@example.com' の部分だけ自分の通知先メールアドレスに書き換えてください。
// --- レシピ1: フォーム回答をGmailで通知 ---
function onFormSubmitGmail(e) {
try {
// --- 通知先メールアドレス(自分のアドレスに書き換える) ---
const toAddress = 'your-address@example.com';
// --- 質問名をキーにした回答オブジェクト ---
const namedValues = e.namedValues;
// --- 件名と本文を組み立て ---
const subject = '[フォーム] 新しい回答が届きました';
const body = Object.entries(namedValues)
.map(function(entry) {
return entry[0] + ': ' + entry[1].join(', ');
})
.join('n');
// --- メール送信 ---
GmailApp.sendEmail(toAddress, subject, body);
} catch (err) {
// --- エラー時もトリガーを止めない(暴走対策) ---
console.log('Gmail通知エラー: ' + err.message);
}
}
コードのポイント解説
e.namedValues: フォーム送信時のイベントオブジェクトeが持っているプロパティで、質問テキストをキー、回答を配列の形で持つオブジェクトです。e.valuesと違って列順に依存しないため、質問の追加・並び替えに強くなりますObject.entries(...).map(...): 全ての質問と回答を「質問: 回答」の形に整形し、改行で連結しますentry[1].join(', '): 回答が配列形式(チェックボックス複数選択など)の場合に備えて、カンマ区切りで結合していますtry/catch: トリガー実行中にエラーが出ても、Googleからのエラー通知メールが暴走しないようにする最低限のガードです(後述)
フォーム送信時トリガーを設定する
コードを貼り付けて保存しただけでは、まだフォーム送信時に動きません。トリガーの設定が別途必要です。
- スクリプトエディタ左メニューの 時計アイコン(トリガー) をクリック
- 右下の「トリガーを追加」をクリック
- 以下のように設定:
- 実行する関数:
onFormSubmitGmail - 実行するデプロイ:
Head - イベントのソース:
スプレッドシートから - イベントの種類:
フォーム送信時
- 「保存」をクリック
- 初回のみ承認画面が表示されるので、自分のGoogleアカウントで承認
承認画面で「このアプリは確認されていません」が出るのは、自作スクリプトでは正常な挙動です。「詳細」→「(プロジェクト名)に移動(安全ではないページ)」をクリックして進めます。
設定後、テスト用に1件フォームを送信して、メールが届けば成功です。
レシピ2|回答が来たらSlackに自動通知する(GAS Webhook)
チームでフォームを運用しているなら、GmailよりもSlackチャンネルへの通知のほうが見落としが減ります。Slack Incoming Webhookを使えば、外部APIキーやアプリ承認の手間なく、チャンネル単位で通知を飛ばせます。
事前準備:Slack Incoming Webhook URLを取得
Slack側で以下の手順でWebhook URLを発行します(管理者権限が必要な場合があります)。
- Slack ワークスペースで「Apps」→「アプリを管理」→「Incoming Webhooks」を検索
- 「Slackに追加」をクリック
- 通知先のチャンネルを選択
- 「Webhook URL」をコピーして安全な場所にメモ(後で使います)
Webhook URLは https://hooks.slack.com/services/T.../B.../... のような形式です。このURLが漏れると誰でもそのチャンネルに投稿できてしまうため、絶対にコードに直書きしないでください。次のH2で扱う「スクリプトプロパティ」で管理します。
コピペで動くサンプルコード
// --- レシピ2: フォーム回答をSlackで通知 ---
function onFormSubmitSlack(e) {
try {
// --- スクリプトプロパティから Webhook URL を取得 ---
const webhookUrl = PropertiesService
.getScriptProperties()
.getProperty('SLACK_WEBHOOK_URL');
if (!webhookUrl) {
throw new Error('SLACK_WEBHOOK_URL が未設定です');
}
// --- 質問と回答を箇条書き形式に整形 ---
const lines = Object.entries(e.namedValues)
.map(function(entry) {
return '• ' + entry[0] + ': ' + entry[1].join(', ');
})
.join('n');
// --- Slack に送信するペイロード ---
const payload = {
text: '*新しい回答が届きました*n' + lines
};
// --- HTTP POST で Webhook に送信 ---
UrlFetchApp.fetch(webhookUrl, {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
} catch (err) {
console.log('Slack通知エラー: ' + err.message);
}
}
このコードをそのまま実行する前に、スクリプトプロパティに SLACK_WEBHOOK_URL を登録しておく必要があります。手順は次のH2「スクリプトプロパティでWebhook URLを安全に管理する」で詳しく説明します。先にスクリプトプロパティを設定してから、レシピ1と同じ手順でフォーム送信時トリガーを onFormSubmitSlack に対して設定してください。
通知メッセージのカスタマイズ例
Slack通知は text だけでも機能しますが、強調や絵文字を入れて見やすくすることもできます。以下のように payload を組み替えるだけです。
// --- 強調と絵文字入りメッセージ ---
const payload = {
text: ':bell: *新しい回答が届きました*',
attachments: [
{
color: '#36a64f',
text: lines
}
]
};
attachments を使うと、左側に色付きの縦線が付いて視認性が上がります。最初は text のみで動かして、慣れてからカスタマイズに進むのがおすすめです。
レシピ3|カテゴリ別の回答件数を集計シートに自動更新する
通知に加えて、回答数のリアルタイム集計まで自動化するレシピです。「会議前にピボットを作り直す」「先週の問い合わせ件数を手動で数える」といった作業が消えます。
集計シートの準備
回答先スプレッドシートに、集計 という名前の新しいシートを作成しておきます(コードで自動作成もできますが、最初は手動で作っておくと挙動が見えやすいです)。
| A列 | B列 |
|---|---|
| カテゴリ | 件数 |
| 質問 | 0 |
| 申込 | 0 |
| 苦情 | 0 |
1行目はヘッダー、2行目以降にカテゴリ名と初期件数(0)を入れておきます。フォームの「カテゴリ」質問で選ばれる選択肢と一致させてください。
コピペで動くサンプルコード
// --- レシピ3: カテゴリ別件数を集計シートで自動カウント ---
function onFormSubmitAggregate(e) {
try {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('集計');
if (!sheet) {
throw new Error('「集計」シートが見つかりません');
}
// --- 回答からカテゴリを取り出す(質問名は実際のフォームに合わせる) ---
const category = e.namedValues['カテゴリ']
? e.namedValues['カテゴリ'][0]
: '未分類';
// --- 集計シート全体の値を取得 ---
const data = sheet.getDataRange().getValues();
let updated = false;
// --- 既存カテゴリ行のカウントを+1 ---
for (let i = 1; i < data.length; i++) {
if (data[i][0] === category) {
sheet.getRange(i + 1, 2).setValue(data[i][1] + 1);
updated = true;
break;
}
}
// --- カテゴリ行が無ければ最終行に追記 ---
if (!updated) {
sheet.appendRow([category, 1]);
}
} catch (err) {
console.log('集計エラー: ' + err.message);
}
}
このコードもレシピ1・2と同じく、フォーム送信時トリガーを onFormSubmitAggregate に対して設定することで自動発火します。
ポイント:
e.namedValues['カテゴリ']の'カテゴリ'部分は、フォームの質問テキストと完全一致している必要があります。質問名を変更したらコードも合わせて変更します- 想定外のカテゴリ(自由記述や新しい選択肢)が来ても、
appendRowで末尾に追加されるので集計から漏れません getDataRange().getValues()でシート全体を一度に読むため、件数が多くてもAPI呼び出しは1回で済みます
関数(COUNTIF)と組み合わせる場合との使い分け
「これ、わざわざGASで書かなくてもCOUNTIFでよくない?」という疑問は当然出ます。実際、単純な件数集計だけならCOUNTIF関数のほうが圧倒的に楽です。GAS実装が活きるのは以下のようなケースです。
| やりたいこと | おすすめ手段 |
|---|---|
| 単純なカテゴリ別件数 | COUNTIF関数 |
| 件数 + Slack通知を同時に | GAS(レシピ2と統合) |
| 条件によって書き込み先シートを分ける | GAS |
| 件数が一定数を超えたらアラート | GAS |
| 関数でセルを汚したくない(参照崩れリスク回避) | GAS |
ロジックが分岐する瞬間にGASの真価が出ます。逆に、純粋に件数だけ見たいなら、まずCOUNTIFで作ってからGASに育てる流れでも遅くありません。
フォーム送信時トリガーの設定方法と注意点
3つのレシピで何度か出てきた「フォーム送信時トリガー」を、ここで一度まとめて整理します。トリガーの設定ミスや重複登録は、GAS自動化で最もハマりやすい落とし穴です。
トリガーの設定手順
- スクリプトエディタ左メニューの時計アイコンをクリック
- 右下「トリガーを追加」
- 設定項目:
- 実行する関数: 動かしたい関数名(例:
onFormSubmitGmail) - 実行するデプロイ:
Head - イベントのソース:
スプレッドシートから - イベントの種類:
フォーム送信時 - 失敗通知設定:
毎日通知を受け取る(デフォルト推奨)
- 「保存」→ 初回のみ承認
設定後、トリガー一覧画面に戻り、狙った関数のトリガーが1つだけ並んでいることを確認してください。
トリガー暴走を防ぐ3つの基本ルール
GAS連携で最もやってはいけないのが「トリガー暴走」です。設定ミス1つで、フォーム1回の送信で何通も通知が飛んだり、エラー通知メールで受信箱が埋まったりします。以下の3つのルールを守れば、大半のトラブルは避けられます。
ルール1: 1関数につきトリガーは1つだけ
開発中に「動かない」と思って何度もトリガーを追加してしまい、気づくと同じ関数のトリガーが3つ並んでいる——あるあるです。1回の送信で同じ通知が3回飛びます。
対策: コードを修正したときは、トリガー一覧画面で古いトリガーが残っていないかを必ず確認してください。重複していたら、不要な行の右側「⋮」→「トリガーを削除」で消します。
ルール2: 関数の冒頭で必ず try/catch
GASのトリガーは、関数内でエラーが出ると「実行失敗通知メール」をGoogleが自動送信します。さらに悪いことに、フォーム送信が連続するとこの通知メールも連続で届き、受信箱が埋まります。
function onFormSubmitGmail(e) {
try {
// --- 本来の処理 ---
} catch (err) {
console.log('エラー: ' + err.message);
}
}
このように try/catch で全体を包み、エラーは console.log で記録するだけにしておくと、トリガーは正常終了扱いになり、暴走通知が止まります。
ルール3: トリガーから別のトリガーを呼び出さない
たとえば「フォーム送信時トリガー」内でセルに値を書き込み、それが別の onEdit トリガーを発火させて……という連鎖は、簡単に無限ループに発展します。
対策: トリガーから呼ばれる関数では、意図しない onEdit 系トリガーが別途設定されていないかを必ず確認してください。1つのスプレッドシートで複数の自動化を組む場合は、トリガー間の依存関係を紙に書き出してから実装するのがおすすめです。
スクリプトプロパティでWebhook URLを安全に管理する
レシピ2のSlack通知で前提となっていた「スクリプトプロパティ」を、ここで詳しく扱います。Webhook URLやAPIキーをコードに直書きするのは絶対NGで、その代替手段がスクリプトプロパティです。
スクリプトプロパティとは
スクリプトプロパティは、GASプロジェクトに紐づいたキーバリュー型の設定保管庫です。コードの外側に値を置けるため、以下のメリットがあります。
- コードをGitHubや社内Wikiに公開しても、Webhook URLが漏れない
- 環境ごと(本番・テスト)に値を切り替えやすい
- スクリプト実行時に
PropertiesService経由で取り出せる
スプレッドシートのセルとは別物なので、通常の操作では値が見えない点も安全性に寄与しています。
設定方法とコードからの呼び出し方
設定はスクリプトエディタの設定画面から行います。
- スクリプトエディタ左メニューの歯車アイコン(プロジェクトの設定)をクリック
- 下にスクロールして「スクリプト プロパティ」セクションへ
- 「スクリプト プロパティを追加」をクリック
- 以下のように入力:
- プロパティ:
SLACK_WEBHOOK_URL - 値:
https://hooks.slack.com/services/...(コピーしたWebhook URL)
- 「スクリプト プロパティを保存」
コードからは PropertiesService.getScriptProperties().getProperty('キー名') で取り出します。レシピ2のサンプルでは以下の3行で読み出していました。
const webhookUrl = PropertiesService
.getScriptProperties()
.getProperty('SLACK_WEBHOOK_URL');
複数の値(例: GMAIL_TO, DISCORD_WEBHOOK_URL)を扱う場合も、全て同じスクリプトプロパティに登録して、関数ごとに必要なキーを取り出すのが定石です。
GoogleフォームとGAS連携でよくあるエラーと対処法
実装中に高確率で出るエラーを、上から順に潰せる形でまとめます。
エラー1: 「TypeError: Cannot read properties of undefined」
原因: e.namedValues['カテゴリ'] のように指定した質問名が、フォームの実際の質問テキストと一致していない。質問名にスペースが含まれていたり、絵文字が入っていたりすると詰みやすいです。
対処: スクリプトエディタで以下のコードを単発実行し、実際の namedValues のキー一覧を確認してください。
function debugCheckKeys() {
const sheet = SpreadsheetApp.getActiveSheet();
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
console.log(headers);
}
ログに表示された通りの文字列を、コードの e.namedValues['...'] に貼り付け直します。
エラー2: 「Exception: Service Spreadsheets failed while accessing document」
原因: スプレッドシートのアクセス権限が不足している、または承認画面で適切に許可されていない。
対処: トリガーを一度削除して、関数を手動実行(再生ボタン)して承認をやり直します。承認画面で「詳細」→「安全ではないページに移動」→「許可」の手順を踏んでください。
エラー3: 「Exception: Request failed for hooks.slack.com returned code 404」
原因: Slack Webhook URLが無効、または削除済み。
対処: Slack側でWebhookを再発行し、新しいURLでスクリプトプロパティを更新してください。muteHttpExceptions: true を付けていると例外は出ませんが、ログには記録されているので console.log で確認できます。
エラー4: 通知が同じ内容で2回・3回飛ぶ
原因: トリガーが重複登録されている。
対処: 時計アイコンからトリガー一覧を開き、同じ関数のトリガーが2行以上ある場合は1つを残して削除します。
エラー5: 「このアプリは確認されていません」が消えない
原因: GAS自作スクリプトの正常な挙動です。Googleの審査を通したアプリではないため、毎回承認のたびにこの画面が出ます。
対処: 「詳細」→「(プロジェクト名)に移動(安全ではないページ)」をクリックして進めてください。自分で書いたスクリプト以外には、絶対にこの操作をしないでください。
まとめ|Googleフォーム×GASで通知と集計を一気に自動化する
ここまで、Googleフォームの回答をGASで連携させる3つのレシピを見てきました。最後にポイントを整理します。
- レシピ1(Gmail通知): 追加設定なしで動く。最初の動作確認に最適
- レシピ2(Slack通知): チームでの共有に強い。Webhook URLは必ずスクリプトプロパティ管理
- レシピ3(集計シート自動更新): COUNTIFでは届かない、条件分岐や通知連動の場面で威力を発揮
- トリガー設定: 「フォーム送信時」を選ぶ。重複登録に注意し、必ず
try/catchで囲む - LINE Notifyは2025年3月末で終了しているため、本記事では非掲載。代替はSlack + Gmailで十分
GASの基本操作(保存・実行・ログ)に不安がある場合は、シリーズ1本目の入門記事で体得しておくと、本記事のコードがより理解しやすくなります。
次のステップとしては、フォームに連動した自動返信メールや、回答内容に応じた条件分岐通知にチャレンジするのがおすすめです。本記事のコードはどれも try/catch と e.namedValues という共通の土台に乗っているので、追加機能は同じ構造の中に書き足していけます。
「気づかない」「集計が手間」がなくなるだけで、Googleフォームの活用範囲は劇的に広がります。まずはレシピ1から、自分の手元のスプレッドシートで動かしてみてください。
