iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

Google Apps Script(GAS)で遊んでみた

こんにちは。iimonでエンジニアをしているhayashiと申します。

普段は主に拡張機能を開発しております。

最近スプレッドシートを弄ることがちょいちょい出てきて、 GASで遊んでみたので、その内容を纏めました。

Google Apps Scriptとは

Google Apps Script (GAS) は、モダンなJavaScriptでGoogle Workspaceと統合するビジネスアプリケーションを素早く構築できるクラウド実行プラットフォームです。

主な特徴:

  • ローカル環境のセットアップが不要(ブラウザのみで開発可能)
  • スクリプトはGoogleのサーバ上で実行され、コードはGoogle Driveに保存される
  • JavaScriptベースなので学習コストが低い

主な用途

カテゴリ できること
UI拡張 Docs / Sheets / Forms にメニュー・サイドバー・ダイアログを追加
計算 Sheets のカスタム関数 / マクロ作成
Web開発 スタンドアロン または Google Sites に埋め込む Web アプリの公開
サービス連携 Gmail / Calendar / Drive 等のクロスサービス自動化
配布 Google Workspace Marketplace にアドオンとして公開

スクリプトの種類

GAS スクリプトには大きく2タイプがあります。

タイプ 作成方法 特徴
コンテナバインド Sheets/Docs/Forms 等から「拡張機能 > Apps Script」 UI拡張・シンプルトリガー (onOpen, onEdit) が利用可能
スタンドアロン script.google.com から新規作成 ファイルに紐付かず単独で存在。権限があれば任意のシート等を操作可

主な違いは「バインドされたファイルに対してシンプルトリガーが使えるか」になりそうです。

用語 意味
コンテナバインドスクリプト 特定のファイルに紐付いて、そのファイルの中に住むスクリプト
スタンドアロンスクリプト どのファイルにも紐付かず、Drive に独立して存在するスクリプト

onOpen / onEdit の挙動 (シンプル vs インストーラブル)

シンプルトリガー (関数名を onOpen / onEdit にするだけのもの) は コンテナバインドでないと動きません。スタンドアロンで同じ挙動が欲しければ、インストーラブルトリガーを使います。

// ❌ スタンドアロンに書いても発火しない (どのファイルを監視するか不明)
function onEdit(e) { /* ... */ }

// ✅ スタンドアロンから特定 Spreadsheet に対してトリガー設置
function setupTriggers() {
  const targetSpreadsheet = SpreadsheetApp.openById('1AbCxxxxxxxxx');
  ScriptApp.newTrigger('handleEdit')    // ← どの関数を呼ぶか (関数名の文字列)
      .forSpreadsheet(targetSpreadsheet) // ← どのファイルを監視するか
      .onEdit()                          // ← どのイベント (編集) を監視するか
      .create();                         // ← 設定を保存
}

function handleEdit(e) {
   const info = {
      日時: new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }),
      タブ名: e.range.getSheet().getName(),
      セル: e.range.getA1Notation(),
      変更前: e.oldValue ? e.oldValue : '(空 or 複数セル)',
      変更後: e.value ? e.value : '(空 or 複数セル)',
      編集者: e.user ? e.user.getEmail() : '不明',
    };

    console.log('編集履歴', info);
}
観点 シンプル (コンテナバインド限定) インストーラブル (両方可)
セットアップ 関数名を書くだけ ScriptApp.newTrigger() で作成
認可付きサービス (Gmail 送信等) ❌ 使えない ✅ 使える
実行時間制限 30秒 6分
監視対象 自分が住むコンテナファイル 任意のファイルを ID 指定
1スクリプトで複数ファイル監視

使い分け判断フロー

このスクリプトの主な仕事は?
├─ 特定1ファイルを開いた/編集した時の処理
│   → コンテナバインド (シンプルトリガー)
├─ Gmail 送信や外部 API 呼び出しを伴う自動処理
│   → スタンドアロン (インストーラブルトリガー)
├─ 時間ベース (毎朝 9 時に実行など)
│   → どちらでも可。スタンドアロンの方が管理しやすい
├─ 複数 Sheets を横断する集計
│   → スタンドアロン
├─ Web アプリ
│   → スタンドアロン (公開 URL 管理がしやすい)
└─ 他スクリプトから呼び出すライブラリ
    → スタンドアロン必須

シート開閉・編集に応じた処理を実装するならコンテナバインド、それ以外はスタンドアロンを選ぶのが基本方針のようです。


基本的な使い方の流れ

  1. エディタを開く:
    1. Sheetsの場合「拡張機能 > Apps Script」(コンテナバインド)
    2. script.google.com で新規作成(スタンドアロン)
  2. コードを書く: JavaScriptで記述
  3. 保存して実行: 実行ボタンを押すと初回はOAuth認可ダイアログが出る
  4. 必要に応じトリガー設定 / Webアプリとしてデプロイ

使用例(コンテナバインド)

それではスプレッドシートで実行するハンズオンをやってみます。

  • Apps Scriptで開く
  • SpreadsheetApp.getActiveSheet().appendRow(['Hello World']) というコードを書いて実行を押すと、Hello World が行に追加されます。
  • 最初は承認が必要です

以下のようにappendRowされます。 引数は配列なので、列を増やしたい場合は要素を追加するだけで対応できます。 getActiveSheet は文字通りアクティブになっているシートを取得するため、シートが複数あると使いづらくなります。基本的には名前で取得するのが一般的みたいです。

// URL: https://docs.google.com/spreadsheets/d/1AbCxxxxxxxxx/edit
//                                                ^^^^^^^^^^^ ← この部分が ID
function myFunction() {
  const sheet2 = SpreadsheetApp.openById('1AbCxxxxxxxxx').getSheetByName('シート2');
  sheet2.appendRow(['シート', 'テスト2']);
}

同じように操作できます。

スプレッドシートの操作

以下の定数を定義してシート2を取得しているとします。

const SHEET_2 = SpreadsheetApp.openById('1AbCxxxxxxxxx').getSheetByName('シート2');

getDataRange().getValues() を使用すると、指定したスプレッドシートのデータを二次元配列で取得できます。

read メソッドを選んで実行すると、 以下のような実行結果が得られます アップデートする場合はループを回して書き換えれば、 以下のようにアップデートできます。操作方法は二次元配列の操作と同じなので、自由に加工できます。 削除も deleteRow で指定行を消したり、clear で全部消したりと、用途別に色々なメソッドが用意されています。 deleteRow で削除すると、行は自動的に詰められます。 トリガーを作成すれば、任意のタイミングでメソッドを実行できます。 「トリガーを追加」ボタンを押して発火タイミングを選択すれば、自動で実行されるようになります。 例えば create がページを検索して結果をシートに書き込むメソッドだとすれば、毎日設定しておくことで、検索結果を元にした自動レポート作成のような運用も可能です。

UI拡張

コンテナバインドのみで使える onOpen を使ってみます。

onOpen メソッドの中で SpreadsheetApp.getUi().createMenu('CRUD操作') を呼び、addItem でメニュー項目を追加し、最後に addToUi を実行すると、

const SHEET_ID = '1AbCxxxxxxxxx';
const SHEET_NAME = 'シート2';

// シートを毎回取得 (onOpen 時には呼ばれない)
function getSheet() {
  return SpreadsheetApp.openById(SHEET_ID).getSheetByName(SHEET_NAME);
}

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('CRUD操作')
    .addItem('Create (行追加)', 'create')
    .addItem('Read (全件をログ出力)', 'read')
    .addItem('Update (シート→シートupdate)', 'update')
    .addItem('Delete (4行目削除)', 'deleteValue')
    .addToUi();
}

function create() {
  getSheet().appendRow(['シート', 'テスト2', 'テスト']);
}

function read() {
  console.log(getSheet().getDataRange().getValues());
}

function update() {
  const sheet = getSheet();
  const data = sheet.getDataRange().getValues();
  data.forEach((d) => {
    if (d[0] !== 'シート') return;
    d[0] = 'シートupdate';
  });
  sheet.getDataRange().setValues(data);
}

function deleteValue() {
  getSheet().deleteRow(4);
}

ヘルプの横にカスタムタブが追加されます。一度onOpenを実行して認可を済ませれば、以降はファイルを開くたびに自動でタブが表示されます。

これでスプレッドシート上から直接スクリプトを実行できるので、非エンジニアの方にも使いやすくなります。 他にも UI 拡張は、ボタンの追加や入力 UI の作成など様々なことが可能です。

スタンドアロン

スタンドアロンで一斉メールを送るスクリプトを作ってみます。

以下のような形でリストがあるとします。 GmailApp.sendEmail でメールを送信できるので、二次元配列をループで回してメールを送る sendEmails メソッドを書いて実行すると、 上記のようにスプレッドシートに記載のアドレスに対してきちんと送信処理が走っていることが確認できます。 メアドは example.com のダミーなので「不明」として返ってきてますが、正常なアドレスなら問題なく送信可能です。

インストーラブルトリガーでonEditを発火

上記の「onOpen / onEdit の挙動 (シンプル vs インストーラブル)」で触れた onEdit を、インストーラブルトリガーで発火させてみましょう。

  • ScriptApp.newTrigger('handleEdit') でトリガー対象のメソッドをセット
  • .onEdit() で編集イベントを監視
  • .create(); で設定を保存
  • そして setupOnEditTriggers メソッドを実行します A11 を編集した場合、 編集履歴を以下のように確認できます (e.user はメールアドレスが含まれてしまうため、今回実行時はコメントアウトしています)。

    まとめ

他にも色々と出来ることがあると思いますが、とりあえず簡単に遊んでみました。 GAS 特有のコマンドがあるだけで、基本的には JavaScript なので学習コストは低そうです。

Google の様々なサービスと連携してスクリプトを実行できるので、ルーティンワークの自動化にとても便利そうですね。

エンジニアが実際に使うならclasp (Command Line Apps Script Projects) というものがあるらしく、それを使えばエディタでの編集や TypeScript での記述、Git でのバージョン管理、自動デプロイといった運用が可能になるみたいなので、本格的に使う際はこちらで書いていくのが良さそうですね。

この記事を読んで少しでも興味を持ってくださった方がいらっしゃいましたら、ぜひカジュアルにお話させていただきたいので、ご応募いただけると嬉しいです!

iimon採用サイト / Wantedly / Green

参照