みなさん、こんにちは!
タカハシ(@ntakahashi0505)です。
Google Apps Scriptの用途としてGmailの特定のメールを取得するというのはけっこう多いですよね。
- スプレッドシートに書き出したり
- チャットに転送したり
などなど、けっこう便利ですし。
前回の記事でもこのようなツールを作成しておりました。
しかし、GASでメールを取得する際に気を付けなければいけない点があります。
それは、Gmailの読み取りについての1日20000件の壁です。
これをオーバーしてGmailから読み取ろうとすると、ひたすらエラーを吐き出して動作しないという切ない状況が起きてしまうんですね。
ということで、今回はこの件について注意しなきゃね、という内容です。
Google Apps ScriptにおけるGmailの読み取り件数の制限とその対処法についてお伝えします。
GASによるGmailの読み書きの制限について
まず、「Quotas for Google Services」のページ
Google Apps Scriptで実行する際の各種動作の制限が一覧されています。
その中で、Gmailに関しては「Email read/write (excluding send)(Gmailの読み取りと書き込み(送信以外))」という項目で、以下のような制限が設けられていることがわかります。
- コンシューマー(gmail.com): 20000 /日
- G Suite Basic / Business: 50000 /日
有料版のG Suiteと無料のGoogleアカウントでは異なりますが、いずれもGmailの読み取り件数には制限があるということをまず押さえておきましょう。
以降本記事では、無料のGoogleアカウントを前提に、20000件が許容量ということで話を進めていきます。
GASでGmailの取得件数がオーバーしてしまう例
searcgメソッドの最大取得スレッド数
例えば、以下のようなスクリプトを想定してみます。
function searchContactMail() { const query = '"このメールは 株式会社プランノーツ http://plannauts.co.jp のお問い合わせフォームから送信されました"'; const threads = GmailApp.search(query); console.log(threads.length); }
スクリプトを仕込んでいるGoogleアカウントのGmailから以下の条件でスレッド検索して、そのスレッド数を求めるというものです。
“このメールは 株式会社プランノーツ http://plannauts.co.jp のお問い合わせフォームから送信されました”
ここで、searchメソッドの引数はクエリだけで、第2,第3引数は省略していることに注目しておいてください。
実行して、ログを確認すると、以下のように「500」スレッドを取得していることがわかります。
ここで、searchメソッドの書式は以下のとおりです。
つまり、第3引数を省略すると、最大500のスレッドを取得してしまうということなんです。
GmailApp.searchメソッドの最大取得数に注意
「1日20000件なんて、そんないかないだろ~」って思いますでしょ?
実はそうじゃないんです。
例えば、15分に一回のトリガーを仕込んだとすると、そのスクリプトは1日で96回呼び出されることになりますよね。
条件にマッチするスレッドが500件以上あり、かつ最大取得数に関する引数を省略してしまったりすると、毎回500スレッドを取得します。
そして、1日で96回呼び出されるわけですから、以下のように余裕でオーバーとなります。
これはいかんですね。設計ミスです。
GmailApp.searchメソッドを使う場合は、その最大取得数に気をつけなければいけないのです。
というか、必ず最大取得数は指定するようにすればOKですね。
まとめ
以上、Google Apps ScriptにおけるGmailの読み取り件数の制限とその対処法についてお伝えしました。
Gmailの読み取り件数に関しては、GmailApp.searchメソッドの最大取得数とイベントトリガーの発火間隔についてはよく注意して設計をして下さいね。
GASはクラウド環境でたいへん便利な一方で、様々な制限が課せられていますね。
ただ、制限内で開発するというのは、けっこうスキル的には向上の機会だったりもするので、ポジティブに捉えています。
また、気を付けて欲しい点があれば、記事にしますね。
どうぞお楽しみに!
連載目次:GASでGmailに届いた問い合わせメールを収集する
お仕事の現場では日々様々なメールが届きます。その中で重要なのが「問い合わせメール」ですね。このシリーズでは、Webサイトからの問い合わせメールを自動でスプレッドシートに取り込んだり、チャットワークに送る方法についてお伝えしていきます。
コメント
はじめまして。貴重な情報をシェアしていただいていつも有難うございます。初めて組んでみたGoogleスプレッドシートのスクリプトでGmailApp.searchの上限に達してしまった様で悩んでいます。Gmailの特定ラベル内のメールをスプレッドシートへインポートするものです。
============================
function mailToSheet () {
// —– ここから書き換え —–
// 書き込むシートURL
var sheet_url = ‘https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxx/edit’;
// 書き込むシート名
var sheet_name = ‘シート1’;
// Gmailの抽出条件
var mail_query = ‘Label:BTA’;
// 存在チェック(既にシートに書き込み済のメールは処理しない(日付、from、to、件名の一致で判定))
var existence_check = true;
// —– ここまで書き換え —–
var ss = SpreadsheetApp.openByUrl(sheet_url);
var sheet = ss.getSheetByName(sheet_name);
var existence_keys = fetchExistenceKeys();
var mail_data = fetchMailData();
for (var i = 0; item = mail_data[i]; i++) {
if (existence_check && existsData(item)) {
continue;
}
sheet.appendRow(itemToRow(item));
}
function fetchExistenceKeys () {
var existence_keys = {};
var sheet_data = sheet.getDataRange().getValues();
for (var i = 0; row = sheet_data[i]; i++) {
existence_keys[generateKey(rowToItem(row))] = true;
}
return existence_keys;
}
function existsData (item) {
if (existence_keys[generateKey(item)]) {
return true;
}
return false;
}
function generateKey (item) {
var key = item[‘date’] + ‘_’ + item[‘subject’] + ‘_’ + item[‘from’] + ‘_’ + item[‘to’];
// Logger.log(key);
return key;
}
function rowToItem (row) {
var item = {};
item[‘date’] = row[0];
item[‘subject’] = row[1];
item[‘from’] = row[2];
item[‘to’] = row[3];
return item;
}
function itemToRow (item) {
var row = [];
row[0] = item[‘date’];
row[1] = item[‘subject’];
row[2] = item[‘from’];
row[3] = item[‘to’];
row[4] = item[‘body’];
return row;
}
function fetchMailData () {
var result = [];
var threads = GmailApp.search(mail_query);
for (var i = 0; it = threads[i]; i++) {
var messages = it.getMessages();
for (var j = 0; message = messages[j]; j++) {
var item = {};
item[‘date’] = message.getDate();
item[‘subject’] = message.getSubject();
item[‘from’] = message.getFrom();
item[‘to’] = message.getTo();
item[‘body’] = message.getBody();
result.push(item);
}
}
return result;
}
}
=======================
このようなスクリプトです。
当初、何もわからずトリガーを5分間隔で設定していましたら、3日目にLimitに達したというエラーが出るようになってしまいました。今は一日一度のトリガーに変更しましたが、それで回避策になるでしょうか?一日に新しい該当メールは1000通ほど届きます。
もし教えていただけるようであれば救われます・・どうぞよろしくお願いいたします。
Fujiiさん
コメントありがとうございます。
GmailApp.searchメソッドで指定している検索条件のクエリがラベルだけで、かつ最大取得数を指定していないので、毎回500スレッドを取得する形になっているものと思います。
対策としては、未読のスレッドだけを拾って処理済みのスレッドは既読にする、処理済みのスレッドには別のラベルをつけてそのラベルは検索条件の対象外にするなどが対策としてはあるかと思います。
記事にもある通り、Gmailの読み取りは一日20000件ですので、1日1回のトリガーなら制限にはかかりません。ただ、1回あたり500件までしか取得しないので、1000通届くなら取得漏れする可能性があるかと思います。
こんにちは、タカハシさん
いつも大変助かっております。
1点質問です。
Gmail内でスレッド化したものは、その最初のメールの内容しか届かないようなのですが、
どうにか切り分ける方法は無いでしょうか?
どうぞよろしくお願いいたします。
人造人間クリリン40号さん
お返事遅くなりましてごめんなさい。
実際におっしゃる通りの仕様になっています。
解決する方法はありますので、別途記事にて紹介したいと思います。
お待ちいただければありがたく思います。
よろしくお願いいたします。