【RPA】Python×受信メール解析|Gmail・IMAP認証・ヘッダー及び本文分析による業務自動化や迷惑メール対策

こんにちは、Kosei(@kay_diacc2)です!

受信メールの確認が手間、メール確認作業を自動化したい」「Pythonで簡易にメール解析できないか」というニーズに答えます。本記事では「Gmailをベースに受信メール解析ツールの開発手順およびプログラミング方法」を詳しく紹介します。Gmailのみならず他プロバイダ(YahooやOutlook)を用いたツール作成にも役立てることができる内容となっております。

目次

受信メール解析ツールとは?

受信メール解析ツールとは、「人手を介さずに受信したメールをチェックし、メールの内容に応じて必要なアクションを自動で行ってくれるツール」です。主な用途としては下記があります。

業務効率化やマーケティング用途でのメール解析

スパム・迷惑メール対策でのメール解析

「メール業務の自動化の意義」「メール自動化でできること」を詳しく知りたい方は、まずこちらの記事をご覧になられることを推奨します。

メール解析の対象要素(ヘッダー・ボディ)について

メールのヘッダーとは、受信したメールがいつ・どこから・どのような経路で送信されたのか記録したものです。メールのボディとは、メールの本文を指します。メール解析では、これらヘッダー・ボディ情報を解析していくことになります。主に取り扱う情報を整理すると下記のようになります。

メールヘッダー:代表的な取り扱い情報

  • 電子メールの識別番号(Message-ID)
  • メール作成時刻(Created At)
  • 送信元(From)のメールアドレス
  • 送信先(To)のメールアドレス
  • 件名(Subject)
  • メール配信経路(Received)
  • メール配信エラー時の戻り先(Return-Path)

メールボディ:代表的な取り扱い情報

  • メール本文

Gmailを用いた受信メール解析ツールの作成手順

上図にメール解析ツール構築の全体プロセスを示します。

まず、IMAPサーバーにログインし、解析したいメールを検索します。続いて、メール情報に応じて必要アクションを付与するといった流れです。今回紹介するプログラミング手法は、メール解析の中核となる①〜③を中心に解説しています。

通信規定の事前整理

下記通信規定の用語は頻繁に用います。事前に確認しておきましょう。

IMAP

IMAP(Internet Message Access Protocol)とは、メール受信のためのプロトコルです。IMAPの場合、メールはサーバ上での保管が特徴であるため、メールのステータス(既読・未読・削除)等も全てサーバで管理されます。

SSL/TLS

SSL(Secure Sockets Layer)およびTLS(Transport Layer Security)は、インターネット上のデータを暗号化し、送受信するためのプロトコルです。個人情報やクレジットカード、パスワードなどのデータを暗号化し、データの改ざんや盗聴を防止します。

必要ライブラリの事前インストール

本記事では、下記ライブラリを使用します。事前にインストールしておきましょう。

IMAPClient(IMAPサーバー接続に利用)

pip install imapclient

backports.ssl(SSL暗号化に利用)

pip install backports.ssl

pyOpenSSL(SSL暗号化に利用)

pip install pyOpenSSL

pyzmail36(メール解析に利用)

pip install pyzmail36

Gmailのアプリパスワードを事前に取得する

Gmailのアプリパスワードを作成していない場合、事前作成が必要です。下記の手順に従い、アプリパスワードを生成しましょう。(※普段Gmailにログインするパスワードとアプリパスワードは別のものです。)

  1. Googleアカウントを開く(上図参照)
  2. セキュリティを開く
  3. 2段階認証プロセスをONにする
  4. アプリパスワードを生成する

受信メール解析ツールを実際に作成

それでは実際にPythonを用いて受信メール解析ツールを作成していきましょう!

IMAPサーバー接続&SSL暗号化

まず、サーバー接続を解説します。メールプロバイダを指定、SSL暗号化し、IMAPClientオブジェクトを生成することでIMAPサーバーに接続します。

# ライブラリ読込
import imapclient
from backports import ssl
from OpenSSL import SSL 

# SSL暗号化
context = ssl.SSLContext(SSL.TLSv1_2_METHOD)

# IMAP接続用のオブジェクト作成
imap = imapclient.IMAPClient("imap.gmail.com", ssl=True, ssl_context=context)

今回はGmailを利用していますが、下記IMAPサーバー名を記載すると別プロバイダーの指定も可能です。日頃から活用しているプロバイダーを指定すると良いでしょう。

プロバイダー SMTPサーバー名
Gmail imap.gmail.com
Yahoo Mail imap.mail.yahoo.com
Outlook.com imap-mail.outlook.com

IMAPサーバーにログイン

IMAPClientオブジェクト生成後、login()メソッドでIMAPサーバーにアクセスします。login()メソッドには、第一引数にメールアドレス、第二引数にパスワードを渡します。

# ログイン情報
my_mail = "メールアドレスを入力"
app_password = "パスワードを入力"

# IMAPサーバーログイン
imap.login(my_mail,app_password)

メール検索&解析

ログイン後、下記の2ステップを跨いで特定のメールを探し出します。

  1. メールを検索する対象フォルダを指定
  2. メール検索キーワードをもとに検索実行

対象の受信メールフォルダを指定

メールアカウントにどのようなフォルダが存在するか確認が必要な場合、以下のように記載することでフォルダ名を確認できます。

# 全てのメールフォルダ情報を表示
imap.list_folders()
# 出力結果
# [((b'\\HasNoChildren',),b'/','INBOX'),
#  ((b'\\HasChildren',b'\\Noselect'),b'/','[Gmail]'),
#  ((b'\\All', b'\\HasNoChildren'),b'/','[Gmail]/All Mail'),
#  ((b'\\Drafts', b'\\HasNoChildren'),b'/','[Gmail]/Drafts'),
#  ((b'\\HasNoChildren',b'\\Important'),b'/','[Gmail]/Important'),
#  ((b'\\HasNoChildren',b'\\Sent'),b'/','[Gmail]/Sent Mail'),
#  ((b'\\HasNoChildren',b'\\Junk'),b'/','[Gmail]/Spam'),
#  ((b'\\Flagged',b'\\HasNoChildren'),b'/','[Gmail]/Starred'),
#  ((b'\\HasNoChildren',b'\\Trash'),b'/','[Gmail]/Trash')]

下記のように、IMAPClientオブジェクトselect_folder()メソッドにフォルダ名を渡し、メール検索対象フォルダを指定します。

# メールフォルダを指定
imap.select_folder("INBOX", readonly=True)

今回第一引数にフォルダ名(“INBOX”)、第二引数にreadonly=True(メールの編集や削除操作をできなくする)と指定します。第二引数はメール確認が目的であれば、通常このように記載します。

メール検索キーワードをもとに、メール検索実行

フォルダ選択後、下記のように記載してメール検索を実行してみましょう。

import pyzmail
import pandas as pd

#① 検索キーワードを設定 & 検索キーワードに紐づくメールID検索
KWD = imap.search(["ここに検索キーワードを記載する"])

#② メールID→メール本文取得
raw_message = imap.fetch(KWD,["BODY[]"])

ここで、メールの検索方法は下記の表を参照ください。

検索方法 検索キーワード記載例
対象フォルダの全メール検索 [“ALL”]
対象期間 [“SINCE”, “開始日”, “BEFORE”, “終了日”]
対象日 [“ON”, “対象日”]
メールアドレス [“FROM”, “差出人メールアドレス”]
[“TO”, “メールアドレス”]
[“CC”, “メールアドレス”]
[“BCC”, “メールアドレス”]
既読ステータス [“SEEN”] (既読)
[“UNSEEN”] (未読)
フラグ(重要)ステータス [“FLAGGED”] (フラグあり)
[“UNFLAGGED”] (フラグなし)
返信ステータス [“ANSWERED”] (返信済み)
[“ANSWERED”] (未返信)
除外(NOT) [“NOT”, “検索したくない条件”]
追加(OR) [“OR”, “追加検索したい条件1″,”追加検索したい条件1”]

以下検索方法を具体的に紹介します。用途に応じて①部分を書き換えましょう。

2021年1月1日〜2021年3月30日まで期間で未読メールを取得

#① 検索キーワードを設定 & 検索キーワードに紐づくメールID検索
KWD = imap.search(["SINCE","01-Jan-2021", "BEFORE", "30-Mar-2021", "UNSEEN"])

2021年1月1日に”test@gmail”から届いたメールを取得

#① 検索キーワードを設定 & 検索キーワードに紐づくメールID検索
KWD = imap.search(["ON","01-Jan-2021", "FROM", "test@gmail.com"])

“test@gmail”または”test2@gmail.com”から届いたメールを全取得

#① 検索キーワードを設定 & 検索キーワードに紐づくメールID検索
KWD = imap.search(["OR", "FROM", "test@gmail.com", "FROM", "test2@gmail.com"])

検索したメールを解析

検索したメール解析結果を出力します。Pandasのデータフレームを用いて宛先、件名、本文の情報を抽出してみましょう。

#解析メールの結果保存用
From_list = []
Cc_list = []
Bcc_list = []
Subject_list = []
Body_list = []

#検索結果保存
for j in range(len(KWD)):
    
    #特定メール取得
    message = pyzmail.PyzMessage.factory(raw_message[KWD[j]][b"BODY[]"])
    
    #宛先取得
    From = message.get_addresses("from")
    From_list.append(From)
    
    Cc = message.get_addresses("cc")
    Cc_list.append(Cc)
    
    Bcc = message.get_addresses("bcc")
    Bcc_list.append(Bcc)
    
    #件名取得
    Subject = message.get_subject()
    Subject_list.append(Subject)
    
    #本文
    Body = message.text_part.get_payload().decode(message.text_part.charset)
    Body_list.append(Body)
    

#④出力
table = pd.DataFrame({"From":From_list,
                      "Cc":Cc_list,
                      "Bcc":Bcc_list,
                      "Subject":Subject_list,
                      "Body":Body_list,
                     })

print(table)

これでメール解析が完了しました。あとは、解析結果に応じて必要アクションを付与するのみです。

本記事において、対応アクションのプログラミング方法は割愛しています。実際の業務ではPandasで得れらた情報をもとにメール集計や情報連携のプログラムを追加してみると良いでしょう。メールの削除方法に関しては以下ご紹介します。

メールの解析結果に対してアクションを付与(メールの削除)

メールを削除するには、IMAPClientオブジェクトdelete_messages()メソッドを活用します。その際、メール検索時に取得したメールID(KWDから取得)を渡し、メールを削除します。

# フォルダを指定
imap.select_folder("フォルダ名を記載",readonly=False)
# 検索実行
KWD = imap.search(["検索方法を記載"])
# 削除
imap.delete_messages(KWD)
# メール完全削除
imap.expunge()

ここで重要な点として、メール検索時にフォルダ指定する際は、readonly=Falseとすることです。これによってメールが削除可能になります。

IMAPから接続を遮断

メール解析が終わったら、IMAPClientオブジェクトlogout()メソッドを用いてIMAPサーバーから遮断しましょう。

imap.logout()

【まとめ】ツールの全体コード紹介

最後に本日学習したコードを関数の形で全量記載します。

第一引数にメールアドレス、第二引数にパスワード、第三引数にフォルダ名、第四引数にメール検索条件を渡す関数です。戻り値として解析したメール情報(宛先情報、件名、メール本文)を渡します。

""" ライブラリ読込 """
import imapclient
from backports import ssl
from OpenSSL import SSL 
import pyzmail
import pandas as pd
pd.options.display.max_columns = None
pd.options.display.max_rows = None

""" (要編集) 引数指定 """
#ログイン情報
my_mail = "メールアドレスを入力"
app_password = "パスワードを入力"

#メール検索条件
FolderName = "フォルダ名を入力"
Search_KWD = ["検索条件を入力"]


""" 関数定義 """
def Get_Mail(my_mail, app_password, FolderName, Search_KWD):
    
    """ ① IMAPサーバー接続 & SSL化 """
    context = ssl.SSLContext(SSL.TLSv1_2_METHOD)
    # context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    imap = imapclient.IMAPClient("imap.gmail.com", ssl=True, ssl_context=context)
    
    """ ② IMAPログイン """
    imap.login(my_mail,app_password)
    
    
    """ ③ メール検索 """
    #対象の受信メールフォルダを指定
    imap.select_folder(FolderName, readonly=True)
    
    #検索キーワードを設定 & 検索キーワードに紐づくメールID検索
    KWD = imap.search(Search_KWD)
    
    #メールID→メール本文取得
    raw_message = imap.fetch(KWD,["BODY[]"])
    
    """  ④ 解析結果保存 """
    #検索結果保存用
    From_list     = []
    Cc_list       = []
    Bcc_list      = []
    Subject_list  = []
    Body_list     = []
    
    #メール解析
    for j in range(len(KWD)):
        #特定メール取得
        message = pyzmail.PyzMessage.factory(raw_message[KWD[j]][b"BODY[]"])

        #宛先取得
        From = message.get_addresses("from")
        From_list.append(From)

        Cc = message.get_addresses("cc")
        Cc_list.append(Cc)

        Bcc = message.get_addresses("bcc")
        Bcc_list.append(Bcc)

        #件名取得
        Subject = message.get_subject()
        Subject_list.append(Subject)

        #本文
        Body = message.text_part.get_payload().decode(message.text_part.charset)
        Body_list.append(Body)


    #Pandas データフレーム
    table = pd.DataFrame({"From":From_list,
                          "Cc":Cc_list,
                          "Bcc":Bcc_list,
                          "件名":Subject_list,
                          "本文":Body_list,
                         })
    
    #IMAPログアウト
    #imap.logout()
    
    return table

最後に以下を実行し、動作確認してみましょう。

# ログイン情報
my_mail = "メールアドレスを入力"
app_password = "パスワードを入力"

# メール検索条件
FolderName = "INBOX"
Search_KWD = ["SINCE","01-Jan-2021", "BEFORE", "30-Mar-2021"]

# 関数実行
Get_Mail(my_mail, app_password, FolderName, Search_KWD)

最後に

この記事が気に入ったら
フォローしてね!

本記事をシェア!
URLをコピーする
URLをコピーしました!
目次
閉じる