
#58 大量のデータを処理するには?
Google Workspace を利用していて、組織を管理する側として GAS を利用していると、以下の URL で案内されているように、一度のプログラムの実行時間の制限が響いてきて、目的の処理を完了させられない場合があります。
例えば、どんなとき?
具体的には、組織内で作成されている Google Classroom のクラスの一覧を作成しようと、GAS のプログラム中で以下の API を用いたような場合です。
一般ユーザーがログインした状態で実行する分には、自身のアカウントが参加しているクラスの一覧が取得でき、以下 URL に規定されているような「参加できるクラス」の最大値に達するような状況でなければ、API を一度実行すればすべての参加しているクラスの状況を取得できるはずです。
しかしながら、全体管理者のアカウントでログインしている状態で上記の API を実行すると、組織内で作成されているすべてのクラスの情報が取得できるため、一度 API を実行しただけでは、すべての結果が得られません。

(https://developers.google.com/classroom/reference/rest/v1/courses/list)
API の結果は、上図のようなクラスの情報が格納された Cource の配列として戻されますが、前述のように大量のデータが結果として返される場合には、nextPageToken に続きを取得するためのトークンが戻されます。
このトークンを指定して、再度 API を実行することで、続きのデータが得られます。
この部分だけを抜粋すると、以下のような感じになります。
do {
// 組織内で作成されているクラスの一覧を取得する
// 参考: https://developers.google.com/classroom/reference/rest/v1/courses/list
let optionalArgs = {
pageSize: 100,
courseStates: 'ACTIVE',
pageToken: nextToken,
};
let res = Classroom.Courses.list(optionalArgs);
if (res.courses != null) {
for (let i = 0; i < res.courses.length; i++) {
// 参考: https://developers.google.com/classroom/reference/rest/v1/courses#Course
let cCourses = res.courses[i];
; // この部分で、必要な処理を行う
}
}
nextToken = res.nextPageToken; // データが継続していれば null 以外が設定されているはず
} while (nextToken != null);
上のプログラムの前に、変数 nextToken には初期値として null を指定しておくと、前回の続きではなく、新規に情報を取得します。
API の結果として得られた変数 res に含まれている res.courses には取得できた分のクラスの情報が格納されており、res.nextPageToken にはデータの続きがあれば必要なトークンが、続きがなければ null が設定されています。
この do while 文のくり返しが何回も実行されることになると、実行時間制限に引っかかってくることになるんです…
どうやって回避する?
再掲すると、GAS の実行時間には下図のような制限が課せられています。

一般の Google アカウントであれば、一回の GAS のプログラムは 6分以上の時間を要する処理を行っていると、途中で中断されてしまいます。
上図では Google Workspace のアカウントでも 6分の制限が課せられるように書かれていますが、実際に利用している感じでは Google Workspace for Education のアカウントでは 30分の制限が課せられているような感じです。
6分が 30分になっても、制限が存在していることには違いないので、大量のデータを処理するときには制限を回避しなければなりません。
以下のページで紹介されていたプログラムを参考にして、実行時間が制限を超えそうになったら、いったん処理を中断して、時間指定のトリガー関数で処理を再開させることにしました。
前述の API で得られる res.nextPageToken のトークンを、スクリプトプロパティに保存しておき、そのトリガー関数で処理を再開するときにスクリプトプロパティからトークンを読みだしてきて、続きのデータを読み込みます。
試しに作ってみた
前述のクラスの一覧を取得するプログラムを試しに作ってみました。
クラスの一覧を取得するプログラムは、過去にも #02 や #35 でも同じようなプログラムを作っていますが、違う観点で焼き直してみました。
実際に動作させていると、「The service is currently unavailable.」といった意味不明なエラーが発生したりもするので、try catch 文で例外処理も追加してあります。
https://docs.google.com/spreadsheets/d/1PlSRnVZxUZdSiIlB7IAOixXgMkb291QBqNXii07lIBQ/copy
上記 URL にアクセスして、メニューから「クラス一覧」→「クラス一覧の作成」を選択すると、シートにクラスの一覧を書き出します。

なお、スクリプトの初回実行時には、実行するアカウントによる確認作業が必要になります。詳しくは以下の投稿をご覧ください。
このプログラムは、何の確認もなく処理を実行します。二回目以降の実行時には、確認もなくシートをクリアして処理を開始します。
30分の制限に引っかからないように、処理を開始してから 25分が経過したら、一旦中断するようにしています。通常の Google アカウントであれば 6分の制限に引っかからないように時間設定してください。
使い勝手よりも、大量のデータを処理することを目的に作ったものなので、複数回に分けて実行された場合に、完全に取得が終了したことは、スプレッドシートを見ているだけではわかりません。
しばらく待っていても、データが追加されなくなったら終わっているはずです… 確実に判断するには、「拡張機能」→「Apps Script」でログを確認することです。不親切なプログラムで、すいません
このプログラムでは、do while 文の中で、クラスの一覧を取得する API を実行していますが、同様にトークンを用いて処理を継続する API に応用できると思います。
こういったプログラムの解決方法を紹介するのが目的のプログラムなので、使い勝手についてはおざなりになっています。
追記 2023/01/21:
プログラムを上記の点に対応させました。詳しくは、以下の記事をご覧ください。 ※配布しているファイルのリンクは同一なので、上記 URL からコピーし直しても同じファイルが得られます。
最後に
最後に、お決まりのフレーズなどを書いておきます。
一応の動作確認は行っているものの、不慮のトラブルによって損害等が生じても、責任はとれませんので予めご了承ください。
コメントを含めても 180行くらいのスクリプトであり、実行に際して目的外の場所への書き出しや収集などは行っていません。
特別なエラー処理は行っていないので、意図しないケースでエラーが発生してしまうかもしれません。どうにもならない場合には、ご連絡ください。
わたし自身にしてみると、このような「スクリプトを作ること」が目的になっているような感じですが、このスクリプトが何かの役に立てば幸いです。