【Python】EmailMessageオブジェクトから件名・宛先・本文を抽出する

目次

概要

Pythonの email モジュールで生成された EmailMessage オブジェクトから、メールの件名、送信元、宛先などのヘッダー情報と、本文(プレーンテキストやHTML)を適切に取り出す方法を解説します。

特にマルチパート形式(HTMLとテキストが混在するメール)から、優先順位を指定して本文を取得するモダンな手法を紹介します。

仕様(入出力)

  • 入力: email.message.EmailMessage オブジェクト
    • message_from_bytes(..., policy=policy.default) 等で生成済みであることを想定)
  • 出力:
    • 件名、送信元、宛先の文字列
    • メール本文の文字列(HTMLまたはプレーンテキスト)

基本の使い方

ヘッダー情報は辞書のようにキーを指定して取得できます。本文は get_body() メソッドを使うと、マルチパート構造を意識せずに取得可能です。

from email import policy, message_from_bytes

# raw_email は受信したバイト列データと仮定
msg = message_from_bytes(raw_email, policy=policy.default)

# 1. ヘッダー情報の取得
print(f"件名: {msg.get('Subject')}")
print(f"送信元: {msg.get('From')}")

# 2. 本文の取得(プレーンテキスト優先)
body = msg.get_body(preferencelist=('plain', 'html'))
if body:
    print(body.get_content())

コード全文

バイト列からオブジェクトを生成し、各種ヘッダーと本文(HTMLがあればHTML、なければテキスト)を抽出して表示する完全な解析コードです。

from email import message_from_bytes, policy
from email.message import EmailMessage

def parse_email_object():
    """
    サンプルのメール生データからEmailMessageオブジェクトを生成し、
    件名・宛先・本文を抽出するデモ関数。
    """
    # テスト用のメール生データ(バイト列)
    # 通常は imaplib 等でサーバーから取得したものを使用します
    raw_email_data = b"""\
MIME-Version: 1.0
Subject: =?utf-8?B?44OG44K544OI44Oh44O844Or44Gu5Lu25ZCN?=
From: sender@example.com
To: receiver@example.com
Content-Type: multipart/alternative; boundary="boundary_text"

--boundary_text
Content-Type: text/plain; charset="utf-8"

これはプレーンテキストの本文です。

--boundary_text
Content-Type: text/html; charset="utf-8"

<html><body><h1>これはHTMLの本文です。</h1></body></html>

--boundary_text--
"""

    # 1. EmailMessageオブジェクトの生成
    # policy=policy.default を指定することで、ヘッダーのデコードや
    # get_body() メソッドなどが利用可能になります(非常に重要)
    msg = message_from_bytes(raw_email_data, policy=policy.default)

    print("--- ヘッダー情報 ---")
    # msg.get(ヘッダー名) で取得。存在しない場合は None が返る
    subject = msg.get("Subject")
    sender = msg.get("From")
    receiver = msg.get("To")
    date = msg.get("Date")

    print(f"件名   : {subject}")
    print(f"送信元 : {sender}")
    print(f"宛先   : {receiver}")
    print(f"日時   : {date}")

    print("\n--- 本文の取得 ---")
    
    # 2. 本文パートの特定 (get_body)
    # preferencelist で取得したい形式の優先順位を指定します
    # ('html', 'plain') -> HTMLがあればHTML、なければTextを取得
    # ('plain', 'html') -> TextがあればText、なければHTMLを取得
    body_part = msg.get_body(preferencelist=('html', 'plain'))

    if body_part:
        # 3. コンテンツの抽出 (get_content)
        # 実際の文字列データを取り出します(自動デコードされます)
        content = body_part.get_content()
        
        # どのタイプが取得できたか確認
        content_type = body_part.get_content_type()
        print(f"取得タイプ: {content_type}")
        print("内容:")
        print(content)
    else:
        print("本文が見つかりませんでした(添付ファイルのみ等の可能性)。")

if __name__ == "__main__":
    parse_email_object()

カスタムポイント

EmailMessageオブジェクトの主要メソッド

解析に使用するメソッドと属性の一覧です。

メソッド・属性処理内容使用例
msg.get("ヘッダー名")指定したヘッダーの値を取得します。policy.default が適用されていれば、MIMEデコード済みの文字列(日本語)が返ります。msg.get("Subject")
msg["ヘッダー名"]辞書形式でのアクセス。get とほぼ同じですが、キーがない場合など挙動が微妙に異なります(通常は get 推奨)。msg["From"]
msg.get_body(preferencelist=...)マルチパートメールの中から、指定した優先順位(html, plain 等)に一致する最初のパート(EmailMessageオブジェクト)を返します。msg.get_body(preferencelist=('plain',))
part.get_content()そのパートのペイロード(中身)をデコード済みの文字列またはバイト列として返します。body_part.get_content()
part.iter_attachments()添付ファイルとして扱われるパートをイテレータとして返します。for f in msg.iter_attachments():

優先リスト (preferencelist)

get_body メソッドの引数 preferencelist には、取得したい subtypetext/html なら html)をタプルまたはリストで指定します。

  • ('html', 'plain'): リッチな表示を優先したい場合。
  • ('plain', 'html'): シンプルなテキスト処理やログ保存を優先したい場合。

注意点

  1. Policyの指定忘れ
    • message_from_bytes(data) のように policy 引数を省略すると、古い compat32 ポリシーが適用され、get_body() メソッドが存在せずエラーになります。また、日本語件名が ?utf-8?... のままになります。
    • 必ず policy=policy.default を指定してください。
  2. 本文がないケース
    • 添付ファイルのみのメールや、空のメールの場合、get_body()None を返します。必ず if body_part: でチェックを入れてください。
  3. エンコーディング
    • get_content()Content-Type ヘッダーの charset パラメータを見て自動的にデコードを行いますが、送信元の設定が誤っていると文字化けやエラー(UnicodeDecodeError)が発生する可能性があります。

応用

HTMLメールしか存在しない場合に、HTMLタグを除去してテキストだけを無理やり抽出する応用例です。

from email import policy, message_from_bytes
import re

def extract_text_forcefully(raw_data):
    msg = message_from_bytes(raw_data, policy=policy.default)
    
    # テキストパートを探す
    body_part = msg.get_body(preferencelist=('plain',))
    
    if body_part:
        return body_part.get_content()
    
    # テキストがなくHTMLしかない場合
    html_part = msg.get_body(preferencelist=('html',))
    if html_part:
        html_content = html_part.get_content()
        # 簡易的なタグ除去(正規表現)
        # 本格的にやるなら BeautifulSoup などを使ってください
        text_content = re.sub('<[^>]+>', '', html_content)
        return text_content.strip()
        
    return ""

まとめ

EmailMessage オブジェクトの解析は、まず policy.default を使って生成することから始まります。

あとは msg.get(“Subject”) でヘッダーを、msg.get_body() で本文を取得するだけで、複雑なMIME構造を意識せずにメールデータをプログラムで活用できます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次