11 第4部 自動化 / GASで手作業をなくす

GAS実践パターン

GASで繰り返し使える「型」を10パターンに整理。トリガーの単純/インストーラブルの違い、定期集計メール、差込送信、onEdit自動記入、getValues/setValues一括処理、UrlFetchApp外部連携、設定保存と例外処理まで実務コード付きで解説します。

読了 約15分 最終更新 2026.06 GASトリガーメール送信UrlFetchApp自動化

GASの実務は、ゼロから書くものではなく「型」を組み合わせるものです。 第10章 GAS入門 で関数の書き方と実行を覚えたら、次は現場で繰り返し登場するパターンを手札にします。本章は、そのまま貼って動く再利用可能な「型」を集めた実践集です。各パターンを「目的→コード→ポイント/注意」の形でまとめました。

本章で扱うパターンは次の10です。

  1. トリガー総論 — 単純トリガー vs インストーラブルトリガー
  2. 時間主導の定期実行 — 毎朝の集計メール
  3. メール自動送信 — シートの行をループして差込送信
  4. onEdit — 編集に応じた自動記入・着色
  5. データ処理の効率化getValues/setValues の一括処理
  6. 外部API連携UrlFetchApp.fetch と Webhook 通知
  7. カスタムメニュー — シート上に実行ボタンを置く
  8. 設定の保存PropertiesService で機密を直書きしない
  9. 例外処理try/catch とログ、失敗通知
  10. クォータ・実行時間の制限 — 上限と回避の考え方

コードは Apps Script(JavaScript)です。構文に迷ったら一次情報の公式リファレンスを確認してください。

1. トリガー総論

GASを「自動で動かす」仕組みがトリガーです。大きく2種類あり、できることが違います。

単純トリガー

特定の名前の関数(onOpen / onEdit / onSelectionChange など)を定義しておくと、対応する操作で自動的に呼ばれます。設定作業は不要ですが、外部サービスへのアクセスができない、認可を必要とする操作はできない、といった制約があります。なお フォーム送信(onFormSubmit)や時間主導は単純トリガーには存在せず、下記のインストーラブルトリガーで登録します(関数名を onFormSubmit にしただけでは自動実行されません)。

インストーラブルトリガー

ScriptApp.newTrigger(...) で明示的に登録するトリガーです。時間主導(毎朝7時など)やイベント駆動を設定でき、メール送信や外部API呼び出しなど認可の要る操作も実行できます

観点単純トリガーインストーラブルトリガー
設定方法決まった名前の関数を書くだけScriptApp.newTrigger で登録
起動契機開く | 編集 | 選択変更時間主導 | 編集 | 開く | フォーム送信 など
外部サービス呼出不可
メール送信不可
時間主導の定期実行不可
インストーラブルトリガーの登録(毎朝7時)

ScriptApp.newTrigger("dailyReport").timeBased().atHour(7).everyDays(1).create();
単純トリガー onEdit の中でメール送信(外部サービス扱いで失敗する)

トリガーの種類と挙動は変更されることがあります。最新の仕様は トリガーの公式ガイド を確認してください。

2. 時間主導の定期実行

目的:毎朝、シートを集計してその結果を自分(やチーム)にメールで送る。

function dailyReport() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("売上ログ");
  const values = sheet.getDataRange().getValues(); // ヘッダ含む全行を一括取得
  let total = 0;
  for (let i = 1; i < values.length; i++) { // 1行目はヘッダなのでスキップ
    total += Number(values[i][2]) || 0;      // C列(金額)を合計
  }
  const body = "本日時点の売上合計は " + total.toLocaleString() + " 円です。";
  MailApp.sendEmail("[email protected]", "売上日報", body);
}

このトリガーは一度だけ登録します(毎回登録すると重複します)。

function setupDailyTrigger() {
  ScriptApp.newTrigger("dailyReport")
    .timeBased()
    .atHour(7)      // 7時台
    .everyDays(1)   // 毎日
    .create();
}

3. メール自動送信

目的:シートの各行(宛先・氏名など)を読み、一人ずつ内容を差し込んでメールを送る。

function sendBulkMail() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("送信リスト");
  const rows = sheet.getDataRange().getValues();
  for (let i = 1; i < rows.length; i++) {
    const name = rows[i][0];   // A列: 氏名
    const email = rows[i][1];  // B列: メールアドレス
    if (!email) continue;      // 空行はスキップ
    const subject = "【お知らせ】6月の予定について";
    const body = name + " 様\n\nいつもお世話になっております。…";
    MailApp.sendEmail(email, subject, body);
  }
}

MailApp.sendEmail(宛先, 件名, 本文) が基本形です。HTMLメールやCC・添付が要るなら、オプションをオブジェクトで渡す形(MailApp.sendEmail({ to, subject, htmlBody, ... }))も使えます。

4. onEdit

目的:セルが編集されたら、隣の列に編集日時を自動で記入する。

function onEdit(e) {
  const range = e.range;            // 編集されたセル
  const sheet = range.getSheet();
  if (sheet.getName() !== "タスク") return; // 対象シートを限定
  if (range.getColumn() !== 4) return;       // D列(状態)の編集だけ反応
  const row = range.getRow();
  if (row === 1) return;                      // ヘッダは除外
  sheet.getRange(row, 5).setValue(new Date()); // E列に更新日時を記入
}

onEdit(e) は単純トリガーで、引数 e に編集イベントの情報(e.range など)が入ります。条件付き書式で十分な着色は数式側に任せ、GASは「値の書き込み」のような数式では難しい処理に使うと役割分担がきれいです。

5. データ処理の効率化

目的:全行に税込価格を書き込む。ここが本章で最重要のパターンです。

セルを1つずつ getValue / setValue で読み書きすると、1回ごとにシートとの通信が発生し、行数が増えるほど劇的に遅くなります。範囲をまとめて getValues で配列に取り、JavaScript側で計算し、setValues で一括書き戻す のが鉄則です。

1000行に税込価格を入れる
1セルずつ回す(遅い・アンチパターン)
for (let r = 2; r <= 1001; r++) { const p = sheet.getRange(r,1).getValue(); sheet.getRange(r,2).setValue(p * 1.1); }

一括取得→配列処理→一括書込(速い)
下のコード参照
function fillTaxIncluded() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("商品");
  const last = sheet.getLastRow();
  const prices = sheet.getRange(2, 1, last - 1, 1).getValues(); // A列を一括取得(2次元配列)
  const result = prices.map(row => [Math.round(row[0] * 1.1)]);  // 配列として一括計算
  sheet.getRange(2, 2, result.length, 1).setValues(result);      // B列へ一括書込
}

getValues / setValues が扱うのは常に 2次元配列(行の配列、各行は列の配列)です。1列でも row[0] のように内側の添字が要る点に注意してください。

6. 外部API連携

目的:外部のWeb APIを叩いてJSONを受け取る/Webhookに通知を送る。

function fetchJson() {
  const res = UrlFetchApp.fetch("https://api.example.com/status");
  const data = JSON.parse(res.getContentText()); // 本文をJSONとして解釈
  Logger.log(data.message);
}

POSTやヘッダ付きの送信はオプションで指定します。次は Slack 等の Webhook へ通知する最小例です。

function notifyWebhook(text) {
  const url = "https://hooks.example.com/xxxx"; // Webhook URL
  const options = {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify({ text: text })
  };
  UrlFetchApp.fetch(url, options);
}

UrlFetchApp.fetch(url, options)optionsmethodcontentTypeheaderspayload を渡せます。レスポンスは getContentText() で本文、getResponseCode() でHTTPステータスを取得できます。詳細は UrlFetchApp リファレンス を参照してください。外部呼び出しは認可が要るため、単純トリガーからは実行できません(パターン1・4)。

7. カスタムメニュー

目的:スプレッドシートのメニューバーに自前の項目を足し、ボタン代わりに関数を実行する。

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("業務ツール")
    .addItem("税込価格を一括計算", "fillTaxIncluded")
    .addItem("日報を送信", "dailyReport")
    .addToUi();
}

onOpen はファイルを開くたびに自動で走る単純トリガーです。addItem("表示名", "関数名") で、メニューを選ぶとその関数が手動実行されます。エディタを開かずに非エンジニアのメンバーが処理を実行できるので、配布シートの定番です。

8. 設定の保存

目的:APIキーや送信先などの設定を、コードに直書きせず安全に保存する。

function saveApiKey() {
  PropertiesService.getScriptProperties().setProperty("API_KEY", "実際のキー");
}

function useApiKey() {
  const key = PropertiesService.getScriptProperties().getProperty("API_KEY");
  // key を UrlFetchApp のヘッダなどに使う
}

PropertiesService.getScriptProperties() はスクリプト単位の保存領域です。ユーザーごとに分けたいなら getUserProperties() もあります。設定値をコードから分離できるため、共有や引き継ぎが安全になります。

9. 例外処理

目的:処理が途中で失敗してもログに残し、必要なら自分に通知する。

function safeJob() {
  try {
    dailyReport(); // 本処理
  } catch (err) {
    Logger.log("失敗: " + err.message); // 実行ログに記録
    MailApp.sendEmail("[email protected]", "GASエラー通知", String(err.stack || err.message));
  }
}

時間主導トリガーは裏で動くため、失敗しても気づきにくいのが弱点です。try/catch で囲み、Logger.log でログに残しつつ、重要な処理は失敗時に自分へメールするとブラックボックス化を防げます(外部送信を伴うのでインストーラブルトリガー前提)。

10. クォータ・実行時間の制限

GASには「1回の実行時間」「1日のメール通数」「UrlFetchApp の呼び出し回数」などの上限があります。これらは変わりうるため具体値は断定しません。無料アカウントと Google Workspace で上限が異なる点も含め、必ず公式の 割り当て・制限ページ を確認してください。

回避の考え方は「一度に全部やらない」ことです。

  • 一括処理で軽くする:そもそも getValues/setValues(パターン5)で実行時間を削るのが最優先。
  • バッチ分割・続きから再開:処理対象が多いときは1回あたりの件数を区切り、どこまで終えたかを PropertiesService(パターン8)に保存します。次回はその続きから処理し、時間主導トリガー(パターン2)で小分けに繰り返します。
続きから再開する型(考え方)

処理した最終行を PropertiesService に保存 → 次回はその次の行から N 件だけ処理
10万行を1回のトリガーで全部処理しようとする(実行時間の上限で途中停止する)

11. 全角・半角をそろえる正規化(カスタム関数)

全角・半角の単純な変換なら、標準関数の ASC(全角→半角)/ JIS(半角→全角)で足ります(第8章 参照)。ただし ASC全角→半角の一方向 で、カタカナも半角に倒します。「半角カナは全角へ、全角英数字は半角へ」と NFKC 規格の標準形に一括で寄せたい 場合は、標準関数だけでは表現しきれません。ここで GAS の出番です。JavaScript の文字列メソッド normalize("NFKC") を使えば NFKC 正規化を一発で行え、カスタム関数にすればセルから =NORMALIZE(A2) のように呼べます。

/**
 * 全角の英数字・記号・半角カナを NFKC 正規化して返すカスタム関数。
 * 単一セルでも範囲でも使える(範囲なら同じ形で配列を返す)。
 * 使い方: =NORMALIZE(A2)  または  =NORMALIZE(A2:A100)
 */
function NORMALIZE(value) {
  // 範囲(2次元配列)が渡された場合は各セルを変換して返す
  if (Array.isArray(value)) {
    return value.map(function (row) {
      return row.map(function (cell) {
        return cell === '' || cell === null ? '' : cell.toString().normalize('NFKC');
      });
    });
  }
  // 単一セル
  return value === '' || value === null ? '' : value.toString().normalize('NFKC');
}

このカスタム関数を整形用の列に置けば、VLOOKUP / XLOOKUP のキー不一致(全角半角ゆれによる #N/A)をまとめて解消できます。=ARRAYFORMULA(NORMALIZE(A2:A)) のように範囲で一括適用するのも有効です。

パターンを支える2つの軸

正しいトリガー選び

外部送信やAPI呼び出しが要るならインストーラブル、編集やオープンへの軽い反応なら単純トリガー。この見極めを誤ると「ローカルでは動くのに自動実行で失敗する」典型的なハマりに陥ります。

一括処理の徹底

getValues/setValues の一括処理は、速度だけでなく実行時間クォータ対策でもあります。すべてのパターンの土台になる最重要の型です。

各パターンを単体で動かせるようになったら、次はそれらをつないで日々の業務フローに組み込む段階です。複数サービスの連携や、安定して動かし続けるための運用は 第12章 連携と運用 で扱います。基礎を振り返るなら 第10章 GAS入門、手作業のテンプレは 第9章 業務テンプレート実例 へ。実際に手を動かすなら 演習で鍛える、用語に迷ったら 用語集、一次情報は 参考資料 を参照してください。