import email
import ssl
import imaplib
from email.header import decode_header, make_header
# https://docs.python.jp/3/library/imaplib.html
# [参考]https://qiita.com/ekzemplaro/items/a35e15865d57372f1d2b
# https://docs.python.jp/3.6/library/email.message.html
# https://docs.python.org/ja/3.7/library/email.compat32-message.html#module-email.message
# https://docs.python.jp/3.6/library/email.parser.html
"""
(1) IMAP4クライアントインスタンスを作成する
* Gmail, Yahoo!メールともに nego_combo = ("ssl", 993)
- 143ポートが暗号化なし
- 暗号化なしとSTARTTLSは動作確認できるサーバがなかった(コードが正しいかも不明)
"""
host = "imap.mail.yahoo.co.jp"
nego_combo = ("ssl", 993) # ("通信方式", port番号)
if nego_combo[0] == "no-encrypt":
imapclient = imaplib.IMAP4(host, nego_combo[1])
elif nego_combo[0] == "starttls":
context = ssl.create_default_context()
imapclient = imaplib.IMAP4(host, nego_combo[1])
imapclient.starttls(ssl_context=context)
elif nego_combo[0] == "ssl":
context = ssl.create_default_context()
imapclient = imaplib.IMAP4_SSL(host, nego_combo[1], ssl_context=context)
imapclient.debug = 3 # 各命令をトレースする
"""
(2) IMAP4サーバにログインする
"""
username = "username@yahoo.co.jp"
password = "password"
imapclient.login(username, password)
"""
(3) メール受信(MIMEメッセージを受信)
"""
imapclient.select() # メールボックスの選択
typ, data = imapclient.search(None, "ALL") # data = [b"1 2 3 4 ..."]
datas = data[0].split()
fetch_num = 5 # 取得したいメッセージの数
if (len(datas)-fetch_num) < 0:
fetch_num = len(datas)
msg_list = [] # 取得したMIMEメッセージを格納するリスト
for num in datas[len(datas)-fetch_num::]:
typ, data = imapclient.fetch(num, '(RFC822)')
msg = email.message_from_bytes(data[0][1])
msg_list.append(msg)
imapclient.close()
imapclient.logout()
"""
受信したメール(MIMEメッセージ)の確認
"""
for msg in msg_list:
print(msg)
"""
各ヘッダや本文を取得する
"""
for msg in msg_list:
# 各ヘッダ情報はディクショナリのようにアクセスできる
from_addr = str(make_header(decode_header(msg["From"])))
subject = str(make_header(decode_header(msg["Subject"])))
# 本文(payload)を取得する
if msg.is_multipart() is False:
# シングルパートのとき
payload = msg.get_payload(decode=True) # 備考の※1
charset = msg.get_content_charset() # 備考の※2
if charset is not None:
payload = payload.decode(charset, "ignore")
print(payload)
print()
else:
# マルチパートのとき
for part in msg.walk():
payload = part.get_payload(decode=True)
if payload is None:
continue
charset = part.get_content_charset()
if charset is not None:
payload = payload.decode(charset, "ignore")
print(payload)
print()
"""備考
※1 get_payload(decode=True)について
cte = msg["Content-Transfer-Encoding"]や
cte = part["Content-Transfer-Encoding"]
でContent-Transfer-Encodingヘッダを取得してデコード方式を決定しても良いが、
get_payload()のdecode引数をTrueにすると、
Content-Transfer-Encodingヘッダに従って勝手にデコードしてくれる。
ただしデコードしてくれるのはquoted-printable または base64のときのみで、
それ以外の場合(別のエンコード方式や、ヘッダがない場合、ペイロード部が
bogus dataを持っている場合 (i.e. bogus base64 or uuencoded data))のときはそのまま返す。
* bogus dataって何だ?
- MAILER-DAEMON@yahoo.co.jpのメールがデコードしきれずにpayloadがバイト列になったので、
そういうのを指すのかもしれない。
※2 get_content_charset()について
charsetがない不親切なメールがたまにあるので注意。
気合でデコードするしかないが、今回のサンプルコードはそのようなメールは考慮していない。
"""