Pythonでメール送信

お知らせやエラー通知などでシステムを運用する上で欠かせないメール送信機能ですが、Pythonには標準ライブラリでSMTPクライアントとなるsmtplibが予め用意されているため簡単にメールを利用することができます。また、メール送信時にはMIME形式のデータを扱う必要がありますが、こちらも標準ライブラリで用意されています。

細かい説明をすっとばしてとりあえずメールを送信したい場合はGメール送信サンプルを参照してください。

smtplibの使い方

smtplibの使い方ですが、基本的にはMIMEを作成して送信するだけなのですが、SMTPサーバー側の設定によりログインなどの処理を必要に応じて追加していくことになります。

認証なしの場合

(通常ありえないですが)まず、LAN内部で開発用のよううな認証なしのSMTPサーバを利用する場合以下のようになります。MIMEは自力で書く必要はなく、標準モジュールを使用して記述することができます。

import smtplib
from email.mime.text import MIMEText

# 送受信先
to_email = "送信先@hoge.com"
from_email = "送信元@fuga.com"

# MIMETextを作成
message = "メール本文"
msg = MIMEText(message, "html")
msg["Subject"] = "メール表題"
msg["To"] = "送り先メールアドレス"
msg["From"] = "送信元メールアドレス"

# サーバを指定する
server = smtplib.SMTP("SMTPサーバ", ポート番号)
# メールを送信する
server.send_message(msg)
# 閉じる
server.quit()

認証ありの場合など

さて、次に認証等がある場合はログイン処理などが必要となります。

ログイン処理

ログイン処理を行う場合、loginメソッドを使用します。

server = smtplib.SMTP("SMTPサーバ", ポート番号)
# 認証を行う
server.login(account, password)

デバッグ

認証失敗などで原因を調べる場合、以下のようにデバッグをTrueにします。

server = smtplib.SMTP("SMTPサーバ", ポート番号)
server.set_debuglevel(True)

以下のようなデバッグが出力されます。

send: 'ehlo xxxx.xxxxxx\r\n'
reply: b'250-smtp.xxxxx.com at your service, [NNN.NNN.NNN.NNN]\r\n'
reply: b'250-SIZE 35882577\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250-STARTTLS\r\n'
reply: b'250-XXXXXX\r\n'
reply: b'250-PIPELINING\r\n'
reply: b'250-CHUNKING\r\n'
reply: b'250 SMTPUTF8\r\n'
reply: retcode (250); Msg: b'smtp.xxxxx.com at your service, [NNN.NNN.NNN.NNN]\nSIZE 35882577\n8BITMIME\nSTARTTLS\nXXXXXX\nPIPELINING\nCHUNKING\nSMTPUTF8'
send: 'STARTTLS\r\n'
:
:
:

STARTTLS

STARTTLSに対応している場合、starttlsメソッドにより元のポートのまま途中から暗号化通信に切り替えることができます。

server = smtplib.SMTP("SMTPサーバ", ポート番号)
server.starttls()

より汎用的に、STARTTLSに対応している場合だけstarttls()を実行したい場合は場合はhas_extnで文字列'STARTTLS'を指定することで判定することが可能です。

if server.has_extn('STARTTLS'):
    server.starttls()

また、ehloは内部で勝手に実行してくれるので気にする必要はありませが、あえて手動で実行したい場合は以下の記述で明示的にehloすることができます。

server.ehlo()

SSL接続

SSLに接続する場合は、SMTP_SSLを使用します。

import ssl
server = smtplib.SMTP_SSL("smtp.gmail.com", 465, context=ssl.create_default_context())

Gメール送信

では、よく使う使うであろう、Gメールでの送信サンプルです。GMailは接続元IPアドレスをDNS逆引きしてドメインが見つからない場合は接続を拒否するため、デフォルトではローカルのPythonからメールを送信することができません。このため、予めアカウント設定から「安全性の低いアプリからのアカウントへのアクセスを許可する」をONにする必要があります。ただし、これはセキュリティ上の危険をはらむため、別途学習用のアカウントを作成することをおすすめします。

 
まず、STARTTLSを使用する場合のサンプルです。

from email.mime.text import MIMEText
import smtplib

# SMTP認証情報
account = "hogehoge@gmail.com"
password = "passpass"

# 送受信先
to_email = "送信先@hoge.com"
from_email = "送信元@gmail.com"

# MIMEの作成
subject = "テストメール"
message = "テストメール"
msg = MIMEText(message, "html")
msg["Subject"] = subject
msg["To"] = to_email
msg["From"] = from_email

# メール送信処理
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(account, password)
server.send_message(msg)
server.quit()

SMTPサーバにsmtp.gmail.com、ポート番号に587を指定し、starttlsを利用します。

 
次にSSLを使用するサンプルです。

import smtplib, ssl
from email.mime.text import MIMEText

# SMTP認証情報
account = "hogehoge@gmail.com"
password = "passpass"

# 送受信先
to_email = "送信先@hoge.com"
from_email = "送信元@gmail.com"

# MIMEの作成
subject = "テストメール2"
message = "テストメール2"
msg = MIMEText(message, "html")
msg["Subject"] = subject
msg["To"] = to_email
msg["From"] = from_email

server = smtplib.SMTP_SSL("smtp.gmail.com", 465, context=ssl.create_default_context())

server.login(account, password)
server.send_message(msg)
server.quit()

SMTPサーバにsmtp.gmail.com、ポート番号に465を指定し、SMTP_SSLを利用します。

MIME関連の補足

最後に補足としてMIME関連のtipsを掲載します。

ファイルを添付

MIMEにはデータ形式の異なる複数のコンテンツを1つのメッセージとして扱うため「マルチパート」というメッセージ形式が用意されており、メールにファイルを添付ファイルする際等に利用します。

Pythonでマルチパートを利用する場合はMIMEMultipartを使用します。

gmailで添付ファイル付きメールを送信するサンプルです。

from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
import smtplib
from os.path import basename

# SMTP認証情報
account = "hogehoge@gmail.com"
password = "passpass"

# 送受信先
to_email = "送信先@hoge.com"
from_email = "送信元@gmail.com"

# MIMEの作成
subject = "テストメール3"
message = "テストメール3"
msg = MIMEMultipart()
msg["Subject"] = subject
msg["To"] = to_email
msg["From"] = from_email
msg.attach(MIMEText(message))


# ファイルを添付
path = "./readme.txt"
with open(path, "rb") as f:
    part = MIMEApplication(
        f.read(),
        Name=basename(path)
    )

part['Content-Disposition'] = 'attachment; filename="%s"' % basename(path)
msg.attach(part)

# メール送信処理
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(account, password)
server.send_message(msg)
server.quit()

メールヘッダの書き換え

email.utilsを使用するとメールヘッダを書き換えることができます。

import email.utils
msg = MIMEText(message, "html")
msg["Subject"] = subject
msg['To'] = email.utils.formataddr(('xxxxx', from_email))

上のサンプルで書き換えた内容は以下のようになります。

Subject: ・・・・
To: xxxxx <・・・・@gmail.com>
From: ・・・・