【Python】メールにファイルを添付して送信する方法(MIMEタイプ自動判定)

目次

概要

Python標準ライブラリ email.message を使用して、PDFや画像などのファイルを添付したメールを作成・送信する方法を解説します。

添付ファイルのデータ形式(MIMEタイプ)を mimetypes モジュールで自動判定させることで、様々な種類のファイルを汎用的に扱える実装を紹介します。

仕様(入出力)

  • 入力:
    • 送信元・送信先情報、件名、本文
    • 添付したいファイルのパス
  • 出力:
    • 添付ファイルが含まれたメールメッセージオブジェクトの生成
    • SMTPサーバーへの送信
  • 前提:
    • 送信可能なSMTPサーバー環境(Gmailなど)があること。
    • ファイルが存在し、読み取り可能であること。

基本の使い方

ファイルをバイナリモードで読み込み、MIMEタイプを特定してからメールオブジェクトに追加する基本的な手順です。

from email.message import EmailMessage
import mimetypes

msg = EmailMessage()
msg.set_content("ファイルを添付します。")

file_path = "report.pdf"

# ファイルのMIMEタイプ(種類)を推測
ctype, encoding = mimetypes.guess_type(file_path)
if ctype is None:
    ctype = "application/octet-stream" # 不明な場合のデフォルト

maintype, subtype = ctype.split("/", 1)

# ファイルを読み込んで添付
with open(file_path, "rb") as f:
    file_data = f.read()
    msg.add_attachment(
        file_data,
        maintype=maintype,
        subtype=subtype,
        filename=f.name
    )

コード全文

添付ファイル付きメールを作成し、ローカルのSMTPサーバー(テスト用)へ送信する完全な実装例です。

実運用時はSMTPホスト設定をGmail等に変更してください。

import smtplib
import mimetypes
import os
from email.message import EmailMessage

def send_email_with_attachment():
    """
    ファイルを添付してメールを送信するデモ関数。
    """
    # 1. メール情報の作成
    msg = EmailMessage()
    msg["Subject"] = "【日報】添付ファイル送付の件"
    msg["From"] = "myself@example.com"
    msg["To"] = "boss@example.com"
    msg.set_content("お疲れ様です。\n本日の報告書を添付いたします。\nご確認ください。")

    # 2. 添付ファイルの処理
    # 例として同じディレクトリにある画像ファイルを想定
    target_file = "monthly_chart.png"
    
    # ファイルが存在するか確認(エラー回避)
    if not os.path.exists(target_file):
        print(f"エラー: ファイル '{target_file}' が見つかりません。")
        return

    # MIMEタイプの判定
    mime_type, _ = mimetypes.guess_type(target_file)
    if mime_type is None:
        # 判定できない場合は一般的なバイナリデータとして扱う
        mime_type = "application/octet-stream"
    
    # type/subtype に分割 (例: image/png -> main=image, sub=png)
    main_type, sub_type = mime_type.split("/", 1)

    try:
        with open(target_file, "rb") as f:
            file_data = f.read()
            file_name = os.path.basename(target_file)
            
            # メッセージに添付データを追加
            msg.add_attachment(
                file_data,
                maintype=main_type,
                subtype=sub_type,
                filename=file_name
            )
            print(f"ファイルを添付しました: {file_name} ({mime_type})")

        # 3. SMTP送信処理
        # ここではローカルのテストサーバー(localhost:1025)を指定
        # 本番環境では smtp.gmail.com:587 などを使用
        smtp_host = "localhost"
        smtp_port = 1025

        print("メールを送信中...")
        with smtplib.SMTP(smtp_host, smtp_port) as smtp:
            # smtp.starttls() # 必要に応じて有効化
            # smtp.login("user", "pass") # 必要に応じて有効化
            smtp.send_message(msg)
            
        print("送信完了しました。")

    except Exception as e:
        print(f"送信中にエラーが発生しました: {e}")

if __name__ == "__main__":
    # テスト用ファイルを作成して実行(動作確認用)
    with open("monthly_chart.png", "wb") as f:
        f.write(b"Dummy Image Data")
    
    send_email_with_attachment()
    
    # 掃除
    os.remove("monthly_chart.png")

カスタムポイント

添付ファイル処理に関連する主要なメソッドとモジュールの仕様です。

MIMEタイプの判定 (mimetypes)

構文意味・処理
mimetypes.guess_type(path)ファイルパス(拡張子)に基づいて、MIMEタイプとエンコーディングのタプル (type, encoding) を返します。
例: 'document.pdf'('application/pdf', None)

添付ファイルの追加 (EmailMessage)

パラメータ意味・設定内容
data (第1引数)ファイルの中身そのものであるバイナリデータ(f.read() の戻り値)。
maintypeMIMEタイプのメインカテゴリ。
例: image, application, text
subtypeMIMEタイプのサブカテゴリ。
例: jpeg, pdf, plain
filenameメール受信側で表示されるファイル名。

解説

  • mimetypes の利点: 拡張子ごとに if ext == '.jpg': ... といった分岐を書く必要がなくなり、コードが汎用的になります。
  • application/octet-stream: 拡張子から種類が特定できなかった場合に使用する「任意のバイナリデータ」を示す汎用タイプです。これを設定しておけば、少なくとも受信側でダウンロード可能なファイルとして扱われます。

注意点

  1. ファイルサイズ制限
    • 多くのメールサーバー(Gmail等)には、添付ファイルを含めたメール全体のサイズ上限(例: 25MB)があります。
    • 動画や高解像度画像を送る場合は、Python側でエラーが出る前にサーバー側で拒否される可能性があります。
  2. ファイルパスとファイル名
    • filename 引数には、フルパスではなくファイル名のみ(os.path.basename 使用)を渡すのがマナーです。フルパスを渡すと、受信者の環境によってはセキュリティ警告が出たり、ファイル名が正しく表示されないことがあります。
  3. バイナリモード
    • 画像やPDFなどの非テキストファイルを扱うため、open() 関数には必ず rb (Read Binary) モードを指定してください。

応用

フォルダ内のすべてのファイルを一括で添付するループ処理の例です。

import os
import mimetypes
from email.message import EmailMessage

def attach_all_files_in_dir(msg, directory):
    """
    指定ディレクトリ内の全ファイルをメールに添付します。
    """
    for filename in os.listdir(directory):
        path = os.path.join(directory, filename)
        
        # ディレクトリはスキップ
        if not os.path.isfile(path):
            continue
            
        # MIME判定
        ctype, encoding = mimetypes.guess_type(path)
        if ctype is None or encoding is not None:
            # 判定不能、または圧縮ファイル等は汎用タイプへ
            ctype = "application/octet-stream"
            
        maintype, subtype = ctype.split("/", 1)
        
        with open(path, "rb") as f:
            file_data = f.read()
            msg.add_attachment(
                file_data,
                maintype=maintype,
                subtype=subtype,
                filename=filename
            )
            print(f"追加: {filename}")

# 使用例
# msg = EmailMessage() ...
# attach_all_files_in_dir(msg, "./documents")

まとめ

EmailMessage クラスの add_attachment メソッドを使えば、複雑なMIME構造を意識せずにファイルを添付できます。

mimetypes モジュールと組み合わせることで、拡張子に応じた適切なファイル形式を自動設定し、スマートなメール送信プログラムを作成しましょう。

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

この記事を書いた人

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

目次