\ お問い合わせはこちら! /

Pythonで電子メール(.emlファイル)を読み込む方法

Pythonを使ってメールファイルを読み込みたい!

このような方に向けて、拡張子.emlファイルの読み込み方をご紹介します。

eml ファイルとは、電子メールのメッセージを保存するためのファイル形式です。

ファイルの中には以下のような情報が含まれています。

.emlファイルの内容
  • 送信者情報
  • 受信者情報
  • 件名
  • 本文
  • 添付ファイル

Python には email という標準ライブラリが用意されていて、これを使えば eml ファイルを読み込むことができます。

eml ファイルを読み込む方法

「eml ファイルを読み込む」ということを分解すると、次のようなステップに分けられます。

  1. eml ファイル全体を読み込む
  2. 送信者情報、件名などの要素を取得する
  3. 本文を取得する

それぞれ順番に解説していきます。

1. eml ファイル全体を読み込む

まずは eml ファイルを、Python で扱えるオブジェクトとして読み込みます。

from email import policy
from email.parser import BytesParser

def read_email(eml_file_path):
    """
    emlファイルを読み込む
    """
    with open(eml_file_path, mode="rb") as f:
        msg = BytesParser(policy=policy.default).parse(f)
    return msg

この read_email() 関数の引数として、拡張子が .eml のファイルパスを渡してあげればパースできます。

戻り値の msg は、Message オブジェクトです。

アノテーションをつけるなら、以下のような書き方になります。

from email import policy
from email.message import Message
from email.parser import BytesParser

def read_email(eml_file_path: str) -> Message:
    """
    emlファイルを読み込む
    """
    with open(eml_file_path, mode="rb") as f:
        msg = BytesParser(policy=policy.default).parse(f)
    return msg

パースとは「ただのテキストファイル」を解析して、Python で認識可能なオブジェクトに変換することです。

2. 送信者情報、件名などの要素を取得する

1. で取得した Message オブジェクトから、以下の情報を取得していきます。

  • 送信者メールアドレス
  • 受信者メールアドレス
  • 件名
  • CC
  • 送信日時
  • メールごとに振られたユニークな識別子

Message オブジェクトは辞書のような方法でアクセスできます。

取得例は以下の通り。

# 送信者メールアドレス
from_ = msg.get("From", None)

# 受信者メールアドレス
to = msg.get("To", None)

# 件名
subject = msg.get("Subject", None)

# CC
cc = msg,get("CC", None)

# 送信日時
date = msg.get("Date", None)

# メールごとに振られたユニークな識別子
message_id = msg.get("Message-ID", None)

もちろん通常の辞書と同様msg["From"]のような形でも取り出せますが、上記では取得できなかったときのためにget()メソッドにNoneを明示しておくことで、安全にアクセスできるようにしました。

なお、メールアドレスはname<sample@name.com>の形式になることがあります。
これを「名前部分」と「純粋なメールアドレス部分」を分けて取得したい場合にはparseaddr()メソッドを使って分離しましょう。

def divide_name_and_mailaddress(mailaddress):
    """
    フルメールアドレス, 純粋なメールアドレス部分を分ける
    """
    try:
        return [parseaddr(mailaddress)[0], parseaddr(mailaddress)[1]]
    except Exception:
        return None

BCC は受信者側から見えないので、msg["BCC"]とすることはまずありません。

3. 本文を取得する

本文の取得には、一工夫が必要です。

大前提として、メールファイルには次の2種類があります。

  • シングルパート
  • マルチパート

それぞれ簡単に説明します。

まずシングルパートは、ただのテキストだけからなるシンプルな構成のものです。

一方のマルチパートは、テキストとHTMLが混在していたり、テキストの他に添付ファイルを含む場合にあたります。

つまり色々なコンテンツがある場合にはマルチパート、単一のコンテンツのメールならシングルパートということになります。

メール本文を読み込むには、マルチパートかシングルパートかを場合分けして処理する必要があるので、以下のように条件分岐を組んで取り出します。

def get_email_body(msg: Message) -> str:
    """
    メール本文を取得する
    """
    if msg.is_multipart():
        # マルチパートメールの場合
        # つまり、添付ありやHTML形式のメールの場合
        for part in msg.walk():
            # コンテントタイプを取得
            content_type = part.get_content_type()
            # Content-Dispositionヘッダの内容を取得する
            content_disposition = str(part.get("Content-Disposition"))

            if content_type == "text/plain" and "attachment" not in content_disposition:
                payload = part.get_payload(decode=True)
                detected = chardet.detect(payload)
                encoding = (
                    detected["encoding"]
                    if detected["encoding"] is not None
                    else "utf-8"
                )
                # エラーの場合は文字化けしてでも文字列を出力
                return payload.decode(encoding, errors="replace")
    else:
        # メールの内容を取得
        payload = msg.get_payload(decode=True)
        # エンコード方式を確認
        detected = chardet.detect(payload)
        # 得られたエンコード方式がnoneならutf-8を、そうでなければ得られたものを採用
        encoding = detected["encoding"] if detected["encoding"] is not None else "utf-8"
        # エラーの場合は文字化けしてでも文字列を出力
        return payload.decode(encoding, errors="replace")

まとめ

emlファイルから情報取得する流れは以下の通りです。

  1. emlファイルをMessageオブジェクトとして取得する
  2. 欲しい情報を辞書のような形で取り出す
  3. 本文はマルチパート、シングルパート別に処理を変える

この流れをおさえた上で本記事の内容を参考にしていただければ、メールデータを簡単に取り出せるはずです。

ご不明な点などありましたら、コメント欄にメッセージいただければご回答させていただきます。

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

シェア・記事の保存はこちら!

この記事を書いた人

karo@プログラマのアバター karo@プログラマ プログラマ

「書くことで人の役にたつ」をモットーに活動中。
本職はプログラマで、Python(Django)が得意。最近ではフロント側の技術に触れる機会も増えてきました。
基本情報技術者試験合格。

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)