chihiroです。
最近Pythonでのメールを送受信に試行錯誤することがあり、ようやく分かってきたので、ここにまとめておきたいと思います。
Pythonでメールを送信する
Python標準ライブラリでメールを送信する場合、
- emailパッケージを使ってMIME文書を作成
- smtplibを使って送信
という手順を踏みます。
emailパッケージははじめはとっつきにくいのですが、 各クラスのインターフェイスは統一感があり、よく練られているので、一度分かってしまえば明快です。 国際化されたヘッダーやテキスト以外のコンテンツの扱いに関しても問題ないので、 "battery inside"なPythonのありがたみを実感できるパッケージだと思います。
基本的な例
テキスト形式のメッセージをlocalhost:25から送信する例です。
# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Utils import formatdate
def create_message(from_addr, to_addr, subject, body):
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = from_addr
msg['To'] = to_addr
msg['Date'] = formatdate()
return msg
def send(from_addr, to_addr, msg):
# SMTPの引数を省略した場合はlocalhost:25
s = smtplib.SMTP()
s.sendmail(from_addr, [to_addr], msg.as_string())
s.close()
if __name__ == '__main__':
from_addr = 'spam@example.com'
to_addr = 'egg@example.com'
msg = create_message(from_addr, to_addr, 'test subject', 'test body')
send(from_addr, to_addr, msg)
MIMETextクラスのインスタンスを作って、text/plainなMIME文書を作ります。 smtplibによる接続・送信は、認証がない場合は単純明快です。
gmailを使って送信してみる
gmailはTLSを使って接続しなくてはならないので、接続と認証の手順が上の例とは異なります。
# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
def create_message(from_addr, to_addr, subject, body):
# 上と同じ
pass
def send_via_gmail(from_addr, to_addr, msg):
s = smtplib.SMTP('smtp.gmail.com', 587)
s.ehlo()
s.starttls()
s.ehlo()
s.login('yourname@gmail.com', 'password')
s.sendmail(from_addr, [to_addr], msg.as_string())
s.close()
if __name__ == '__main__':
from_addr = 'yourname@gmail.com'
to_addr = 'egg@example.com'
msg = create_message(from_addr, to_addr, 'test subject', 'test body')
send_via_gmail(from_addr, to_addr, msg)
日本語を含んだメール
emailパッケージのデフォルトの文字セットは'us-ascii'なので、 日本語を含んだメールを送信する場合は、明示的に指定する必要があります。
plain/textのMIME文書の場合は、MIMETextオブジェクトのコンストラクタで文字セットを指定するのが一番簡単です。 それに加えて、メールのヘッダーは、email.HeaderのHeaderオブジェクトを使って国際化しなくてはなりません。
# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate
def send(from_addr, to_addr, msg):
# 上に同じ
pass
def create_message2(from_addr, to_addr, subject, body, encoding):
# 'text/plain; charset="encoding"'というMIME文書を作ります
msg = MIMEText(body, 'plain', encoding)
msg['Subject'] = Header(subject, encoding)
msg['From'] = from_addr
msg['To'] = to_addr
msg['Date'] = formatdate()
return msg
if __name__ == '__main__':
from_addr = 'spam@example.com'
to_addr = 'egg@example.com'
msg = create_message2(from_addr, to_addr, u'テスト', u'本文', 'ISO-2022-JP')
send(from_addr, to_addr, msg)
DoCoMoに絵文字入りのメールを送る
文字セットにShift_JISを使用すると、DoCoMo端末に対して絵文字を含んだメールを送信することができます。
ただし、Pythonのemailパッケージは、入力文字コードとしてShift_JIS, EUC-JPを選択した場合、 自動的に出力文字コードをISO-2022-JPに変換しようとするので、強制的にShift_JISで送信するには追加の処理が必要です。
以下は、"NO Erlang[絵文字:ひらめき] NO Life[絵文字:ハート(複数)]"というメールを送信するサンプルです。 DoCoMoでは、"0xf90xa0"が「ひらめき」の絵文字、"0xf90x94"が「ハート(複数)」の絵文字です。
# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate
from email import Charset
# ヘッダーをQuoted-Printable, ボディーをbase64でエンコードします
Charset.add_charset('shift_jis', Charset.QP, Charset.BASE64, 'shift_jis')
# 文字コードの変換には、"shift_jis"ではなく"cp932"を使っています
Charset.add_codec('shift_jis', 'cp932')
def send(from_addr, to_addr, msg):
# 上に同じ
pass
def create_message_for_docomo(from_addr, to_addr, subject, body):
msg = MIMEText(body, 'plain', 'shift_jis')
msg['Subject'] = Header(subject, 'shift_jis')
msg['From'] = from_addr
msg['To'] = to_addr
msg['Date'] = formatdate()
return msg
if __name__ == '__main__':
from_addr = 'spam@example.com'
to_addr = 'your_email_address@docomo.ne.jp'
body = 'NO Erlang\xf9\xa0No Life\xf9\x94'
subject = 'test'
msg = create_message_for_docomo(from_addr, to_addr, subject, body)
send(from_addr, to_addr, msg)
ポイントは、Charset.add_charsetでshift_jisの設定を上書きしていることと、 Charset.add_codecで明示的にコーデックを指定することで、shift_jisのエンコード、 デコードにcp932を使用するようにしていることです。
SoftBankに絵文字入りのメールを送る
一方、SoftBankには、UTF-8を使うと絵文字入りメールを送信できるようです。 SoftBankの技術資料「絵文字一覧」に、各絵文字のユニコードが公開されているので、これを参考に、ユニコード文字列の本文をUTF-8にエンコードしてMIME文書を作ります。 これさえ分かってしまえば、難しいことはありません。
例えば、蛇の絵文字はユニコードだとU+E52Dなので、"アイ ラブ Python[絵文字:蛇]"という文書ならば、次のようにします。
# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate
def send(from_addr, to_addr, msg):
# 上に同じ
pass
def create_message_for_softbank(from_addr, to_addr, subject, body):
msg = MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = from_addr
msg['To'] = to_addr
msg['Date'] = formatdate()
return msg
if __name__ == '__main__':
from_addr = 'spam@example.com'
to_addr = 'your_email_address@softbank.ne.jp'
body = u'アイラブ Python\ue52d'.encode('utf-8')
subject = 'test'
msg = create_message_for_softbank(from_addr, to_addr, subject, body)
send(from_addr, to_addr, msg)
DoCoMoの例と違い、Charset.add_charsetを行っていませんが、これは、UTF-8を使用した場合、 初めから「ヘッダーにはQuoted-Printableかbase64, 本文にはbase64」でエンコードするように定義されているためです。
添付付きメール・デコメール
添付ファイル付きのメールを送るには、MIMEMultipartクラスのインスタンスをつくり、 MIMEText, MIMEAudio, MIMEImage等のMIMEオブジェクトをattachしていきます。
添付付きメールを送るだけでは捻りがないので、HTMLメール(DoCoMoのデコメール)の送信例を紹介します。
# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.Header import Header
from email.Utils import formatdate
def send(from_addr, to_addr, msg):
# 上に同じ
pass
HTML_BODY = """<html>
<body>
<img src="cid:a"><br>
Hello World<br>
<img src="cid:c"><br>
Welcome to the world of Python<br>
<img src="cid:b"><br>
<body>
</html>"""
def create_deco(from_addr, to_addr):
msg = MIMEMultipart()
msg['Subject'] = Header(u"デコメ", 'ISO-2022-JP')
msg['From'] = from_addr
msg['To'] = to_addr
msg['Date'] = formatdate()
related = MIMEMultipart('related')
alt = MIMEMultipart('alternative')
related.attach(alt)
content = MIMEText(HTML_BODY, 'html', 'ISO-2022-JP')
alt.attach(content)
for filename in ['a', 'b', 'c']:
fp = file('%s.gif' % filename, 'rb')
# Content-Type: image/gif; name="a.gif"
img = MIMEImage(fp.read(), 'gif', name=filename)
# HTMLからイメージを参照するにはContent-IDが必要です
img['Content-ID'] = '<%s>' % filename
related.attach(img)
msg.attach(related)
return msg
if __name__ == '__main__':
from_addr = 'spam@example.com'
to_addr = 'your_email_address@docomo.ne.jp'
msg = create_deco(from_addr, to_addr)
send(from_addr, to_addr, msg)
送信結果は次のようになります。
デコメールに関しては以前にharukiさんが 詳しい資料を公開していますので、興味のある方はこちらもご覧下さい。
おまけ
泥臭いサンプル集になってしまいましたが、それなりに動くWebアプリケーション、 Webサイトを作ろうとするならば、もう少し洗練された解決策が必要です。
- Twistedのtwisted.mail.smtpを使えば、非同期IOを利用して効率よくメールの送信を行えそうです。
- 開発版Djangoのdjango.core.mailモジュールは大幅にリファクタリングされて、メッセージの作成と送信を分離できるようになりました。Djangoをインストールしておけば、もちろんDjangoのテンプレートエンジンを利用できるので、「テンプレートからメッセージの作成」「MIME文書作成」「送信」という処理をきれいに行うことができそうです。email, smtplibパッケージの使用例としても参考になります。
僕自身まだ研究中で、いろいろ試行錯誤している段階なのですが、 もう少し整理できたならば、またこのブログで紹介したいと思います。
また、自分自身いろいろ調査していて、 Python標準の「ライブラリリファレンス」 がやはり一番参考になると実感しています。こちらも合わせてどうぞ。