Gmailの検索と内容取得をするGAS

Gmailでの特定のメール検索とメール内容取得を行うGoogleAppsScriptを紹介します。

概要

GoogleAppsScriptでのメール検索についての紹介と、検索結果のメール内容をスプレッドシートに書き出すGoogleAppsScriptを作成します。
GAS自体は書き出しを行うスプレッドシートに紐づける形で作成することをおすすめします。
GASの準備については以前の記事の「スプレッドシートに紐づいたGASの作成方法」の項目を参考にしてください。

1. メール検索について

GASでのメール検索にはGmailApp.search(query)GmailApp.search(query, start, max)の2種類があります。
両方に共通するqueryが検索条件にあたり、startmaxは検索した結果をどこからどこまで取得するかを指定します。
つまりGmailApp.search(query, start, max)GmailApp.search(query)の発展形といえます。

query:検索条件の指定(詳細は後述)
start:検索結果を何件目から取得するか
max:検索結果を何件分取得するか

例:未読のメールスレッドを1件目から10件分取得

const threads = GmailApp.search('is:unread', 0, 10);

メールスレッドについて

GmailApp.search(query)GmailApp.search(query, start, max)で得られる結果はどちらも「メールスレッドの配列」という形になります。
下記の条件に一致する場合、Gmailが自動的に1つのスレッドとしてメールをまとめます。
当てはまらない場合はそのまま1つのメールで1スレッド扱いになります。

(1) 受信者、送信者、件名が以前のメール(1週間以内に送信されている)と同じである
(2) 最初のメールとその返信、またその返信といった一連のメール群

例:1つのメールだけのスレッド(上)、複数のメールが1つのスレッドになっているメール(下)

そして、「メールスレッドの配列」はこれらのスレッド達のデータでできた配列を指します。
「メールスレッドの配列」の扱い方については後述します。

2. 検索条件について

実際にGASのコードを書く前に、検索条件について見てみましょう。
メール検索の際のqueryは文字列で指定し、複数条件で検索する際は条件同士の間にスペースを入れます。
間にスペースを入れる複数条件検索はAND検索となり条件A 条件Bの場合は条件Aかつ条件Bの結果がヒットします。
OR検索については後述の検索テクニックをご確認ください。

例:test@example.comを含む送信元かつ未読のメールスレッドを検索

const query = 'from:test@example.com is:unread'
const threads = GmailApp.search(query);

指定できる検索条件について紹介していきます。
また、この検索条件や記述方法は通常のGmail画面での検索と同じ仕様です。
GASで検索条件を設定する前に、Gmailの検索でその検索条件で大丈夫なのか確認してみると安心です。

キーワード検索

単語や文章などで検索します。
件名や添付ファイル名など検索エリアを絞って検索も可能です。

任意の文字列
指定した文字列を件名、送信元、送信先、本文等に含むメールを検索します。
例: example ⇒ exampleという文字列を含むメールを検索します。

subject:
件名を部分一致で検索します。
例: subject:example ⇒ exampleという文字列を件名に含むメールを検索します。

filename:
添付ファイルの名前を部分一致で検索します。
例: filename:example ⇒ exampleという文字列をファイル名に含む添付ファイルのあるメールを検索します。

アドレス検索

送信元や送信先、CCやBCCなどをメールアドレスまたは名前の一部で検索します。

from:
送信元をメールアドレスまたは名前の部分一致で検索します。
例: from:test@example.com ⇒ test@example.comを含む送信元からのメールを検索します。

to:
送信先をメールアドレスまたは名前の部分一致で検索します。

cc:
CCをメールアドレスまたは名前の部分一致で検索します。
例: cc:test@example.com ⇒ test@example.comを含むCCへのメールを検索します。

bcc:
CCをメールアドレスまたは名前の部分一致で検索します。
例: bcc:test@example.com ⇒ test@example.comを含むBCCへのメールを検索します。

メールの状態・情報で検索

既読/未読や添付ファイルの有無で検索します。

is:unread
未読メールを検索します。

is:read
既読メールを検索します。

has:attachment
添付ファイルありのメールを検索します。

送信された時間で検索

メールの送信日時を用いて検索します。

after:newer:
指定日以降に送信されたメールを検索します。
例: after:2021/1/10 ⇒ 2021年1月10日以降に送信されたメールを検索します。

before:older:
指定日以前に送信されたメールを検索します。
例: before:2021/1/10 ⇒ 2021年1月10日以前に送信されたメールを検索します。

newer_than:
指定した年数・月数・日数を遡った時点から現在までに送信されたメールを検索します。
例: newer_than:5d ⇒ 5日前から現在までに送信されたメールを検索します。
例: newer_than:4m ⇒ 4カ月前から現在までに送信されたメールを検索します。
例: newer_than:3y ⇒ 3年前から現在までに送信されたメールを検索します。

older_than:
指定した年数・月数・日数を遡った時点から現在までに送信されたメールを検索します。
例: older_than:30d ⇒ 30日前より以前に送信されたメールを検索します。
例: older_than:6m ⇒ 6カ月前より以前に送信されたメールを検索します。
例: older_than:2y ⇒ 2年前より以前に送信されたメールを検索します。

主な検索条件は以上です。
他にもメールのラベルや重要マークの有無、メールサイズの大小での検索も可能です。
それらについては公式ヘルプをご確認ください。

検索テクニック

OR検索や除外検索などの検索テクニックについて紹介します。

ORまたは{}
複数の条件いずれかを含む結果がヒットします。
例: subject:example OR from:test@example.com ⇒ exampleを含む件名のメール(送信元は問わない)と、test@example.comから送信されたメール(件名は問わない)を検索します。
例: {subject:example from:test@example.com} ⇒ exampleを含む件名のメール(送信元は問わない)と、test@example.comから送信されたメール(件名は問わない)を検索します。

-
指定した文字列をもつ結果を除外します。
例: 〇〇株式会社 -メルマガ ⇒ 〇〇株式会社という文字列を含んでかつ、メルマガという文字列を含まないメールを検索します。
例: subject:-example from:test@example.com ⇒ exampleという文字列を含まない件名のメールでかつ、test@example.comから送信されたメールを検索します。

( ) 検索条件をグループ化します。
例: subject:(〇〇株式会社 -メルマガ) ⇒ 件名に〇〇株式会社という文字列を含んでかつ、メルマガという文字列を含まないメールを検索します。
例: subject:〇〇株式会社 subject:-メルマガ ⇒ 件名に〇〇株式会社という文字列を含んでかつ、メルマガという文字列を含まないメールを検索します。(上記と同じ)

""
指定したキーワードまたはフレーズに完全一致するメールを検索します。
ここでいう完全一致は一般に言われる他の文字列を含めないという意味ではなく、あくまで指定したキーワードそのもので検索するという意味です。

検索記号処理回避:
日本語ではという文字がAND検索の記号として用いられるのを回避するためなどに用います。
例: 鮭とばという単語を検索したいのにまたはを含むメールがヒットしてしまう ⇒ "鮭とば"と指定すればOK。
英語の検索であればA or Bという文章を検索したいのに、AまたはBを含むメールがヒットしてしまうのを避ける為のものです。
例: Orange or Lemonという文を検索したいのにOrangeまたはLemonを含むメールがヒットしてしまう ⇒ "Orange or Lemon"と指定すればOK。

類似ワード回避:
通常の検索ではしばし類似のキーワードがヒットしてしまうことがあり、これを避けるためにも完全一致検索を用います。
例: testingという単語を検索したいのにtestを含むメールがヒットしてしまう ⇒ "testing"と指定すればOK。

3. メール内容の書き出しGAS

では実際に検索条件を用いてGASでメールを検索、取得してみましょう。

メールの検索・スレッド群の取得

まずは先述のGmailApp.search(query)を用いてメール検索を行います。
ここで取得できるのはスレッドの集まり、つまりスレッド群なのでthreadsという定数に格納します。
なぜ定数なのかと言うと、このthreadsは今後参照するだけのデータ(書き換えや代入を行う予定がないデータ)だからです。

今回は件名に「おしらせ」を含む未読のメールを検索します。
お手元で試す際はこの条件に限らず、検索条件をちょうどいいものに変えてみてください。

function searchMails() {
  const query = 'subject:おしらせ is:unread'
  const threads = GmailApp.search(query);
}

スレッド群からスレッド、メッセージの抽出

先述の通りスレッド群はスレッドの配列という形のデータです。
スレッドに対して.getMessages()を行うことでメッセージ(=メール1件)の配列が得られます。
下図のような構造のデータになっています。

メッセージ(=メール1件)ごとにデータを取り出したいので、forEachを用います。

function searchMails() {
  const query = 'subject:おしらせ is:unread';
  const threads = GmailApp.search(query);
  threads.forEach(thread => {
    const messages = thread.getMessages();
    messages.forEach(message => {
      // ここにメッセージ毎に対する処理を記述
    });
  });
}

forEachとは:

配列に対して行える処理のひとつで、GASではなくJavascriptに含まれる機能です。
Javascript以外にもC++やPython、PHPなど広く一般に用いられています。
forEachは配列の中のデータひとつひとつに同じ処理を行うためのものです。

forEachの使い方:

配列.forEach((データ, 順番, 配列※)=>{ 処理 }) というような書き方をします。
配列:forEach処理を行う配列
データ:配列の中のデータ1つ1つを指します
順番:現在処理しているデータが配列の中で何番目に存在するデータかを指します。0番目~(省略可)
配列※:forEach処理を行っている対象の配列(省略可)
今回の例では順番、配列※は使用しないため省略しています。

forEachの例:
['りんご', 'バナナ', 'みかん']という配列に対して、それぞれのデータを実行ログに表示する例です。
こちらでは順番も用いてみました。

function sample() {
  const fruitsList = ['りんご', 'バナナ', 'みかん'];
  fruitsList.forEach((fruits, index) => {
    console.log('配列の'+ index + '番目は' + fruits);
  });
}

実行結果:
下記のように配列の内容を1つずつ取り出して処理を行うことができます。

console.log(data)について:

GASでいうと実行ログの部分に指定したデータや結果を表示できます。
エラーが発生したときにこれを使って原因調査を行ったり、正しく値が取得できているかの確認に用いたりします。
文字列と変数を合わせて表示する際などは上記コード例のように+で繋いであげる必要があります。

メッセージの内容をスプレッドシートに書き出し

メッセージ(=メール1件)の内容をスプレッドシートに書き出します。
その際、件名や送信元、本文、添付ファイルをそれぞれ別の列に書き出したいと思います。

スプレッドシート:

メッセージから情報の取得

まずは件名や送信元等をそれぞれメッセージから取り出します。

添付ファイルの処理について:(15行目~)
添付ファイルattachmentsは配列の形で取得しますが、添付ファイルがないメールの場合は空の配列となります。
まず添付ファイルがあるかどうかを判定し、添付ファイルがあった場合はattachmentListに添付ファイルのファイル名を格納します。
その後全ての添付ファイル名を配列attachmentListに格納したら、今度は.join()を用いてこれを文字列に変換します。

.join()について:

配列.join(区切り文字)とすることで、配列⇒文字列のデータ型変換ができます。
例: [‘あいう’,‘abc’,‘イロハ’].join(‘@’) ⇒ ‘あいう@abc@イロハ’

function searchMails() {

  const query = 'subject:おしらせ is:unread'
  const threads = GmailApp.search(query);

  threads.forEach(thread => {

    const messages = thread.getMessages();

    messages.forEach(message => {

      let fromData = message.getFrom(); // 送信元
      let subject = message.getSubject(); // 件名
      let body = message.getPlainBody(); // 本文
      let attachments = message.getAttachments(); // 添付ファイル群(配列)

      let attachmentList = []; // 添付ファイルのファイル名格納用の配列

      if(attachments.length > 0){
        attachments.forEach(attachment => {

          let name = attachment.getName();

          attachmentList.push(name);
        });
      }

      // 配列⇒文字列に変換(','で区切り)
      attachmentList = attachmentList.join(',');

    });
  });
}

スプレッドシートに書き出せる形に整形

次に、取り出した各種データをスプレッドシートに書き出せる形(配列)に整えます。(30行目)
配列は[送信元, 件名, 本文, 添付ファイル名たち]という形にしたいと思います。

function searchMails() {

  const query = 'subject:おしらせ is:unread'
  const threads = GmailApp.search(query);

  threads.forEach(thread => {

    const messages = thread.getMessages();

    messages.forEach(message => {

      let fromData = message.getFrom(); // 送信元
      let subject = message.getSubject(); // 件名
      let body = message.getPlainBody(); // 本文
      let attachments = message.getAttachments(); // 添付ファイル群(配列)

      let attachmentList = []; // 添付ファイルのファイル名格納用の配列

      if(attachments.length > 0){
        attachments.forEach(attachment => {

          let name = attachment.getName();

          attachmentList.push(name);
        });
      }

      attachmentList = attachmentList.join(',');

      // 書き出し用データ(配列)
      let data = [fromData, subject, body, attachmentList];

    });
  });
}

スプレッドシートに書き出し

まず、書き出しを行うスプレッドシートとシートオブジェクトを取得します。(6行目~)
そして.appendRow()を用いて書き出しを行います。(36行目~)

function searchMails() {

  const query = 'subject:おしらせ is:unread'
  const threads = GmailApp.search(query);

  // 書き出しを行うスプレッドシートとそのシート
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('メールリスト');

  threads.forEach(thread => {

    const messages = thread.getMessages();

    messages.forEach(message => {

      let fromData = message.getFrom(); // 送信元
      let subject = message.getSubject(); // 件名
      let body = message.getPlainBody(); // 本文
      let attachments = message.getAttachments(); // 添付ファイル群(配列)

      let attachmentList = []; // 添付ファイルのファイル名格納用の配列

      if(attachments.length > 0){
        attachments.forEach(attachment => {

          let name = attachment.getName();

          attachmentList.push(name);
        });
      }

      attachmentList = attachmentList.join(',');

      let data = [fromData, subject, body, attachmentList];

      // 書き出し(行追加)実行
      sheet.appendRow(data);

    });
  });
}

sheet.appendRow(data)について:

sheetオブジェクトに対して、行の追加ができるメソッドです。
行の追加位置は既にデータのある最下段のひとつ下になります。
つまり、10行目までデータがあった場合11行目としてデータが追加されます。
追加するデータは配列の形式でA列から順に書き込まれます。

実行結果:
このようにメールの内容が取得できました。

書き出し前に過去データの削除

検索したメールの書き出しはできるようになりましたが、このままでは実行するたびに行がどんどん増えていってしまいます。
これを回避するために、書き出しを行う前に既にシートにあるデータを削除することにします。
シート内のデータ削除には色々な方法がありますが、今回は見出し行(1行目)を残しておきたいので.deleteRows(rowPosition, howMany)を用いたいと思います。(9行目~16行目)

function searchMails() {

  const query = 'subject:おしらせ is:unread'
  const threads = GmailApp.search(query);

  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('メールリスト');

  // 削除の開始行と削除する行数を取得(詳細後述)
  const rowPosition = 2;
  const howMany = sheet.getLastRow() - 1;

  // データの行がある場合、行削除を実行(詳細後述)
  if(howMany > 0){
    sheet.deleteRows(rowPosition, howMany);
  }

  threads.forEach(thread => {

    const messages = thread.getMessages();

    messages.forEach(message => {

      let fromData = message.getFrom(); // 送信元
      let subject = message.getSubject(); // 件名
      let body = message.getPlainBody(); // 本文
      let attachments = message.getAttachments(); // 添付ファイル群(配列)

      let attachmentList = []; // 添付ファイルのファイル名格納用の配列

      if(attachments.length > 0){
        attachments.forEach(attachment => {

          let name = attachment.getName();

          attachmentList.push(name);
        });
      }

      attachmentList = attachmentList.join(',');

      let data = [fromData, subject, body, attachmentList];

      sheet.appendRow(data);

    });
  });
}

sheet.getLastRow() - 1について:(11行目)

sheet.getLastRow()で、sheet内でデータの存在する最終行を取得できます。
例えば、10行目までデータがあるシートであれば 10 という数字が取得できます。
ここから-1つまり見出し行分を引くことで、メールデータのある行数を取得できることになります。
しかし、1行もメールデータがなかった場合 0-1 となってしまうため注意が必要です。
今回の例ではメールデータが0件以上のときだけ行削除をするようにしています。(14行目)

sheet.deleteRows(rowPosition, howMany)について:(15行目)

sheetオブジェクトに対して、指定した行から任意の行数分を削除できます。
rowPosition:開始行です。1行目からなら 1 、2行目からなら 2 を指定します。
howMany:削除する行数です。3行分削除するなら 3 、10行分削除するなら 10 を指定します。
例: sheet.deleteRows(5, 6) ⇒ 5行目から6行分(5~10行目)を削除します。

まとめ

Gmailの検索と検索条件の紹介、検索で取得したメールのスプレッドシートへの書き出し方法を紹介しました。
今回は簡単な内容ということでメールの書き出し前に以前のデータは行ごと削除するようにしましたが、工夫すればメール内容を確認して重複しないように行追加なども可能です。
他にも例えばトリガーを用いて、定期実行でメールの書き出しをすることもできます。
是非いろいろと応用して活用してみてください。

Google Cloudの導入は当社にご相談ください

ITディストリビューターであるTD SYNNEXはGoogle Cloud™ Partner Award を受賞するなど、長年にわたりGoogle™のグローバル認定ディストリビューターとして、総合的な Googleソリューションを提供しています。お客様にとって最適なソリューションの提案や導入、活用をサポートします。

製品・サービスについてのお問合せ

情報収集中の方へ

導入事例やソリューションをまとめた資料をご提供しております。

資料ダウンロード
導入をご検討中の方へ

折り返し詳細のご案内を差し上げます。お問い合わせお待ちしております。

お問い合わせ