はじめに

Google Apps Script (GAS) は Web ブラウザ上の JavaScript とも Node.js とも環境が異なります。その違いや、 GAS 特有の機能を理解するとさらに開発が捗るので、それらをこの記事で紹介しようと思います。

Tokyo GAS で 5 分 LT した内容です。

スライドはこちら > GAS ビギナーが GAS を使いこな すために知るべきこと 10 選

1. ローカル環境で開発する

GAS を好きなエディタで開発したり、ローカルで開発したものを github に上げたりしたくなる。

Google ブログの記事 Apps Script による高度な開発プロセス で紹介されているが、 node-google-apps-script という npm パッケージをローカル環境にグローバルインストールことで、ローカルで開発したものを GAS に push することができる。

node-google-apps-script
// clone
$ gapps clone <fieldId>
// push
$ gapps push

clasp というライブラリだと push だけでなく pull もできたり、コマンドも豊富なので、 clasp の方が良いかも。

clasp
// clone
$ clasp clone <fieldId>
// push
$ clasp push
// pull
$ clasp pull

2. github でソースコードを管理する

  1. の方法でローカルで開発する場合は、その状態を github と連携してソースコード管理すれば良い。もう一つの方法としては、 Google Apps Script Github アシスタント という Chrome Extension があるので、これを使えば、 github に push / pull することができる。

スクリーンショット 2018-04-01 10.11.53.png

3. 複数の *.gs 間で関数を呼び出す

GAS では複数の *.gs ファイルを持つことができる。これらの関数はグローバル関数となるので、そのまま他のファイルで呼び出すことができる。以下のように書くと util.gs の add 関数は正常に呼び出せる。

main.gs
function main() {
  var num = add(1, 2);
  Logger.log(num);
}
util.gs
function add(a, b) {
  return a + b;
}

グローバル関数となるので、複数ファイルで同一の名前の関数名をつけてはならない。ちなみに、GAS では console.log() ではなく Logger.log() でログ表示をする。

4. 様々な実行方法

GAS では、主に以下の実行方法から選択する。

  • トリガー
  • G Suite アプリケーションの特定のイベントをハンドリング
  • Web 公開

トリガー

毎週月曜の朝に実行するような場合はトリガーを利用する。 Heroku でいう Scheduler みたいなもの。タイマー(分/時/日/週/月)を利用して、実行タイミングを細かく指定できる。編集 > 現在のプロジェクトのトリガー から設定する。

スクリーンショット 2018-03-21 16.43.37.png

トリガーの例: Google Apps Script (GAS) で毎週 30 分の雑務を自動化した話

G Suite アプリケーションの特定のイベントをハンドリング

  • 特定の Docs を開いた時
  • スプレッドシートの更新時
  • Google フォームの回答時

など、様々なタイミングで実行することができる。公式の Event Objects
で補足できるイベントと、イベント発火時に渡される値を知ることができる。

Web 公開

公開 > ウェブアプリケーションとして導入 を選択すると、公開することができる。簡単な API を作って公開することができ、これはかなり強力。

スクリーンショット 2018-03-21 17.54.30.png

公式の Web Apps の通り、GET の場合は doGet 関数、 POST の場合は doPost 関数を実装する必要がある。ミニマムな GET, POST API 実装を以下に記述する。

minimum-api.gs
function doGet(e) {
  return ContentService.createTextOutput("hello world!");
}

function doPost(e) {
  return ContentService.createTextOutput("OK");
}

返り値は TextOutput か HtmlOutput のいずれかである必要がある。 HtmlService と絡めると、 html を公開できるので、簡単な Web アプリケーションを提供することも可能。

Web 公開の例: 3 分で作る無料の翻訳 API with Google Apps Script

5. スクリプト毎のデータストアがある

前回の実行結果など、スクリプト毎に簡単なデータを保存する保存領域が用意されている
。公式ドキュメントはこちら Class PropertiesService | Apps Script

以下のように呼び出せる。

var properties = PropertiesService.getScriptProperties();
properties.getProperty(key);// 取得
properties.setProperty(key, value);// 設定
properties.deleteProperty(key);// 削除

これで、スクリプトの途中結果を保存して、次の実行時に利用する値を参照できる。

6. スクリプトからトリガーをセットできる

TriggerBuilder を使ってスクリプトからもトリガーを設定できる。以下のコードは、 1 分後main という名前の関数を実行するトリガーを登録している。

var functionName = 'main';
var d = new Date();
d.setMinutes(dt.getMinutes() + 1);// 1 分後
ScriptApp.newTrigger(functionName).timeBased().at(dt).create();

7. 実行時間の制限と対策

1 回の実行で 6 分間の時間制限があり、スクリプトの実行途中でも強制的に終了する。 6 分を超える場合には以下のような対策が取れる。

例として、スプレッドシートに対して各行処理するスクリプトだとすると

  1. 実行時間が 5 分になるのを検知
  2. 次回処理を開始する行数を PropertiesService で保存
  3. TriggerBuilder を使って新しいトリガーを 1 分後に設定
  4. 処理を終了させる

これは、上の 5, 6 で紹介した Tips を用いた方法。 5 分や 1 分という値は仮なので、よさげな値を使ってください。

その他の制限は公式ガイドの Quotas を参照。

8. G Suite 以外のサービスの API をたたく

GAS は G Suite のアプリケーションを簡単に操作できるが、UrlFetchApp を使って外部サービスとの連携も簡単にできる。

例として Slack にメッセージ投稿するコードを以下に示す。

Slack連携
function postSlack(slackWebhookUrl) {
  var body = { text: 'message' };
  var payload = JSON.stringify(body);
  var res = UrlFetchApp.fetch(slackWebhookUrl, {
    method: 'POST',
    headers: { "Content-Type": 'application/json' },
    payload: payload
  });
}

9. Web スクレイピングの方法

GAS における Web スクレイピングは以下の流れで行う

  1. UrlFetchApp.fetch で html を取得
  2. html を parse

1 は直前で紹介したので省略する。 2 については、 GAS 公式の Parsing HTML で紹介された方法で Web ブラウザ上の JavaScript のような HTML のパースができるが、 XmlService を使っているため、エラーになる場合も多い。

エラーになる時は独自の抽出ロジックを実装する必要がある。以下はその一例。

var html     = UrlFetchApp.fetch(url).getContentText();
var username = find(html, '<p id="username">', '</p>'),

/** 
 * 特定の文字列の間に挟まれた文字列を抽出する
 * @param { string } text 検索対象となる文字列
 * @param { string } from 前方の文字列
 * @param { string } to   後方の文字列
 * @return { string } 検索結果
 */
function find(text, from, to) {
  var fromIndex = text.indexOf(from);
  if (fromIndex === -1) return '';
  text = text.substring(fromIndex + from.length);
  var toIndex = text.indexOf(to);
  if (toIndex === -1) return '';
  return text.substring(0, toIndex);
}

ちなみに、スクレイピングのマナーで1 秒間に 1 回しかアクセスしない というものがあるが、 GAS で sleep するには、 Utilities クラスの sleep を呼び出す。そもそもクローリング拒否してるかどうかは robots.txt を見て判断する。

Utilities.sleep(1000);

Utilities は GAS に適した便利関数群が用意されているので、一度眺めてみるのを勧める。

10. GAS ライブラリの仕組み

GAS で npm モジュールを使えれば非常に便利なのだが、 それはできない。

代わりに GAS にはライブラリという仕組みがあり、他の人が作った GAS ライブラリを呼び出したり、自分の GAS を登録することができる。詳しくは公式の Libraries に書かれている。

スクリーンショット 2018-03-21 18.22.02.png

速度が遅いので、代わりにアドオン使えと言われてそうなので、今後はアドオンの流れになるかも(書いてる最中に知った)。

まとめ

ここ数ヶ月で GAS を始めて、調べていて有益だと思った情報をまとめました。私もまだまだ勉強中なので、またネタを仕入れたら記事書きます。

他の GAS ネタ