Pythonでのメール送信
Pythonを使ってメール送信の機能を使う場面としては、AWS LambdaやGCPのCloudFunctionsなどで、お知らせやエラー通知などの通知系で使う機会が多いです。
Pythonにはメール送信を行うための機能として、smtplibというSMTPクライアントのライブラリが標準として提供されています。
メール送信時にはMIME形式のデータを扱う必要がありますが、こちらもemailというライブラリが標準で提供されています。
特にサードパーティ系のライブラリをインストールする必要はないため、Python自体が動く環境があれば今回のサンプルコードを動かすことは可能です。
Pythonのバージョンは3.8を使用していますが、3系であれば基本的に動作には問題ないと思います。
必要なライブラリのインポート
まずはメール送信に必要なライブラリをインポートします。上記で説明したsmtplibとemailの2つです。
from smtplib import SMTP
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.utils import formatdate
また、メール送信時にはMIME形式のデータを扱う必要がありますが、こちらも標準ライブラリで用意されています。
メール本文の作成処理
from email.mime.text import MIMEText
from email.utils import formatdate
まずはメール本文を作る部分を作っていきます。
MIME形式のデータを送信するため、emailライブラリにあるMIMETextを使用します。もう一つは時間を扱うためのformatdateも使用します。
コードをまとめて読みやすくするためにcreateMIMEText
というMIMEText形式のオブジェクトを作成して返すための独自のメソッドを作成していきます。
引数にはメール送信元となるfrom、メールの宛先となるto、メッセージ本文のmessage、メール件名となるsubjectの4つをとります。
def createMIMEText(from, to, message, subject):
"""
処理部分
"""
今回、createMIMEText
メソッド内で使うMIMETextクラスに与える引数はmessageとなるメールの本文です。
第二引数にはplain
、第三引数utf-8
を指定します。文字コードとなる第三引数はデフォルトでは"us-ascii"
になっています。
msg = MIMEText(message, "plain", "utf-8")
HTML形式のメールを送りたい場合は、第二引数はhtml
にします。
msg = MIMEText(message, "html", "utf-8")
本文以外の残りのsubject、from、toは以下のようにキーワード引数を用います。
- 件名は
Subject
- 送信元メールアドレスは
from
- 宛先メールアドレスは
To
- 日付は
Date
日付の記述方法に関しては上記でインポートしたformatdate()を使用します。
msg = MIMEText(message, "html")
msg["Subject"] = subject
msg["From"] = from
msg["To"] = to
msg['Date'] = formatdate()
formatdateの中身を見てみると以下のように出力されます。
print("formatdate()", formatdate())
# formatdate() : Sat, 05 Feb 2022 05:49:43 -0000
メソッドのコード全体
コード全体は以下のようになります。createMIMEText
を呼び出すための呼び出し元となる処理も書いておきましょう。
def createMIMEText(from, to, message, subject):
# MIMETextを作成
msg = MIMEText(message, "html")
# msg = MIMEText(message)
# msg = MIMEText(message, "plain", 'utf-8')
msg["Subject"] = subject
msg["From"] = from
msg["To"] = to
msg['Date'] = formatdate()
return msg
# メールの送り主
from_email = "source@fuga.com"
# メール送信先
to_email = "destination@hoge.com"
# メール件名とメール本文
subject = "メール件名"
message = "メール本文"
mime = createMIMEText(from_email, to_email, message, subject)
print("mime", mime)
createMIMEText
の戻り値となるmimeの中身をprint文で出力してみると以下のようになるはずです。
mime Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Subject: =?utf-8?b?44Oh44O844Or5pys6aGM?=
From: source@fuga.com
To: destination@hoge.com
Date: Sat, 05 Feb 2022 06:01:01 -0000
44Oh44O844Or5pys5paH
メール送信の処理を作っていく
次にメール送信用の処理を作ります。
こちらでもメール送信のためのsend_email
という独自のメソッドを作成し、引数にはMIMEText
オブジェクトのmsgを取ります。
def def send_email(msg):
"""
処理部分
"""
基本的なメソッドの中身としては以下のようになります。
しかし、以下のこの処理ではSMTPサーバの認証処理が無い場合でしか動作しません。通常、SMTPサーバには認証処理を行わなければいけないからです。
from smtplib import SMTP
def send_email(msg):
host = 'SMTPサーバのホスト名'
port = 25
# サーバを指定する
server = SMTP(host, port)
# メールを送信する
server.send_message(msg)
# 閉じる
server.quit()
SMTPサーバの認証
メールを送信するためにもSMTPサーバの認証処理も書いていいます。
メールの内容が暗号化されていないSMTPSやSTARTTLSを使用しない場合は、以下の処理だけになります。単純にlogin
メソッドを呼び出すだけです。
# SMTPサーバのユーザ名とパスワード
account = ""
password = ""
host = 'SMTPサーバのホスト名'
port = 25
server = SMTP(host, port)
server.login(account, password)
STARTTLSに対応させる処理
暗号化の一つであるメールをSTARTTLSに対応させる場合はlogin
の前に、以下のようにstarttls
メソッドを呼び出します。
server = SMTP(host, port)
server.starttls()
STARTTLSが何かがわからない方は以下でわかりやすく解説しています。
STARTTLSの詳細は以下がわかりやすいです。
一般的にSTARTTLSは587番ポートを使用する場合が多いので、587をここでは指定しています。
# SMTPサーバのユーザ名とパスワード
account = ""
password = ""
host = 'SMTPサーバのホスト名'
port = 587
server = SMTP(host, port)
server.starttls()
server.login(account, password)
場合によってはサーバがSTARTTLS
に対応していないことも考えられます。接続先のサーバがSTARTTLSに対応しているかを確かめるメソッドも用意されています。
以下のようなコードでサーバがSTARTTLS
に対応している場合は、サーバにSTARTTLSで接続して暗号化処理を開始します。
if server.has_extn('STARTTLS'):
server.starttls()
# SMTPサーバのユーザ名とパスワード
account = ""
password = ""
host = 'SMTPサーバのホスト名'
port = 587
server = SMTP(host, port)
if server.has_extn('STARTTLS'):
# ehloは内部で勝手に実行してくれるが、あえで手動で実行したい場合は明示的にechoすることができる
server.ehlo()
server.starttls()
server.ehlo()
server.login(account, password)
SSL/TLSに対応させる処理
STARTTLS
だけでなく、SSL/TLS
を使用したSMTPSのメール送信の方法も解説します。
SSL/TLS暗号化通信の場合は465番ポートを使用します。
通常のSMTPでは通信内容が暗号化されず平文のままメールを送信します。しかし、平文のままでは盗聴されるリスクもあるため、SSL/TLS
を使い通信内容を暗号化したSMTPSがあります。
以下の処理はSMTPSを利用したメール送信の方法です。
import ssl
from smtplib import SMTP_SSL
host = 'SMTPサーバのホスト名'
host = "smtp.gmail.com"
port = 465
context = ssl.create_default_context()
server = SMTP_SSL(host, port, context=context)
メールヘッダの書き換え
emailライブラリにあるutilsを使用して、メールヘッダを書き換えることができます。
import email.utils
msg = MIMEText(message, "plain", "subject")
msg["Subject"] = subject
msg['To'] = email.utils.formataddr(('xxxxx', from_email))
上記の処理を行うと以下のようにメールヘッダが書き換えられます。
Subject: =?utf-8?b?44Oh44O844Or5pys6aGM?=
From: source@fuga.com
To: xxxxx <estination@hoge.com>
メールに添付ファイルを追加する
メールを送信する際には画像やpdfなどのファイルを添付することがあると思います。ファイル添付をするには以下のライブラリをインポートする必要があります。
emailライブラリのMIMEMultipart
を使用します。
from email.mime.multipart import MIMEMultipart
ファイルを添付するコードは以下のようになります。
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# メール本文
message = ""
msg = MIMEMultipart()
msg['Subject'] = '件名を入力'
msg['From'] = from_mail
msg['To'] = to_mail
msg.attach(MIMEText(message, 'plain', 'utf-8'))
# 添付ファイルの設定
filename = '添付ファイル名'
path = '添付ファイルのPATH'
with open(path, 'r') as fp:
attach_file = MIMEText(fp.read(), 'plain')
attach_file.add_header(
"Content-Disposition",
"attachment",
filename=filename
)
msg.attach(attach_file)
上記で作成したcreateMIMEText
に添付ファイルを追加するコードをまとめてみます。createMIMETextに引数を追加して、添付ファイルの引数がある場合のみ添付ファイルを追加する処理を行うようにします。
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def createMIMEText(from, to, message, subject, filepath=None, filename=""):
# MIMETextを作成
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = from
msg['To'] = to
msg.attach(MIMEText(message, 'plain', 'utf-8'))
# 添付ファイルの設定
if filepath:
path = filepath
with open(path, 'r') as fp:
attach_file = MIMEText(fp.read(), 'plain')
attach_file.add_header(
"Content-Disposition",
"attachment",
filename=filename
)
msg.attach(attach_file)
return msg
コード全体の完成図
コード全体は以下のようになります。
上記で解説した部分の処理をまとめるために2つほどメソッドを作成しました。
ここではSMTPS(SSL/TLS)
を使用してのコードになります。
import ssl
from smtplib import SMTP, SMTP_SSL
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formatdate
def createMailMessageMIME(from, to, message, subject, filepath=None, filename=""):
# MIMETextを作成
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = from
msg['To'] = to
msg.attach(MIMEText(message, 'plain', 'utf-8'))
# 添付ファイルの設定
if filepath:
path = filepath
with open(path, 'r') as fp:
attach_file = MIMEText(fp.read(), 'plain')
attach_file.add_header(
"Content-Disposition",
"attachment",
filename=filename
)
msg.attach(attach_file)
return msg
def send_email(msg):
account = "アカウント名"
password = "パスワード"
host = 'SMTPサーバのホスト名'
port = 465
# サーバを指定する
# server = SMTP(host, port)
context = ssl.create_default_context()
server = SMTP_SSL(host, port, context=context)
# ログイン処理
server.login(account, password)
# メールを送信する
server.send_message(msg)
# 閉じる
server.quit()
# メールの送り主
from_email = "source@fuga.com"
# メール送信先
to_email = "estination@hoge.com"
subject = "メール件名"
message = "メール本文"
# MIME形式の作成
mime = createMailMessageMIME(from_email, to_email, message, subject)
# メールの送信
send_email(mime)
参考資料
Discussion
参考にさせていただきました!ありがとうございます!
コード全体完成図のところ、最後のcreateMailMessageMIME関数名はcreateMIMETextが正しいかしら?
ご指摘いただきありがとうございます。
完全に誤字だったので、修正しました!