目次
概要
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 には、取得したい subtype(text/html なら html)をタプルまたはリストで指定します。
('html', 'plain'): リッチな表示を優先したい場合。('plain', 'html'): シンプルなテキスト処理やログ保存を優先したい場合。
注意点
- Policyの指定忘れ
message_from_bytes(data)のようにpolicy引数を省略すると、古いcompat32ポリシーが適用され、get_body()メソッドが存在せずエラーになります。また、日本語件名が?utf-8?...のままになります。- 必ず
policy=policy.defaultを指定してください。
- 本文がないケース
- 添付ファイルのみのメールや、空のメールの場合、
get_body()はNoneを返します。必ずif body_part:でチェックを入れてください。
- 添付ファイルのみのメールや、空のメールの場合、
- エンコーディング
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構造を意識せずにメールデータをプログラムで活用できます。
