Pythonでメール受信; imaplibのサンプルコード

# 関連リンク
- [Pythonでメール受信; poplibのサンプルコード
](https://qiita.com/takey/items/a7e843255da8052c64b8)
- [Pythonでメール送信; smtplibのサンプルコード](https://qiita.com/takey/items/6e45a203f1c055a9024c)

# 処理の流れ
1. IMAP4クライアントインスタンスを作成する
2. IMAP4サーバにログインする
3. メール受信（MIMEメッセージを受信）

# サンプルコード

```python
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がない不親切なメールがたまにあるので注意。
    気合でデコードするしかないが、今回のサンプルコードはそのようなメールは考慮していない。
"""
```