OS-X SieraとPython3.6.0でGmailのメールを取り出すスクリプトを書いています。

文字コードがiso-2022-jpのメールはbodyをデコードできるのですが、UTF-8の場合、デコードに失敗します。

出力結果を見る限り、文字コードの取得は成功しているようなのですが、なぜUTF-8で失敗するのか、分かりません。

どなたか解決方法をご教示頂けないでしょうか。よろしくお願い申し上げます。


実行例

1 : テストメール1
2017/03/23 15:25:28
iso-2022-jp

1行目。
2行目。

2 : テストメール2
2017/03/23 17:44:33
utf-8
CjHooYznm67jgIIKMuihjOebruOAggo=


ソース

import imaplib
import email
import os
import sys
import dateutil.parser

def main():
    gmail = imaplib.IMAP4_SSL("imap.gmail.com")

    try:
        gmail.login("xxxxxxx@gmail.com","*******")
        gmail.select('Notes')
        typ, [data] = gmail.search(None, "(ALL)")

        #取得したメール一覧の処理
        cnt = 0

        for num in data.split():
            cnt += 1
            ### 各メールへの処理 ###
            result, d = gmail.fetch(num, "(RFC822)")
            raw_email = d[0][1]
            msg = email.message_from_bytes(raw_email)

            #文字コード取得用
            msg = email.message_from_string(raw_email.decode('iso-2022-jp'))
            msg_encoding = email.header.decode_header(msg.get('Subject'))[0][1] or 'iso-2022-jp'

            #パースして解析準備
            msg = email.message_from_string(raw_email.decode(msg_encoding))

            date = dateutil.parser.parse(msg.get('Date')).strftime("%Y/%m/%d %H:%M:%S")

            subject = email.header.decode_header(msg.get('Subject'))
            title = ""
            for sub in subject:
                if isinstance(sub[0], bytes):
                    title += sub[0].decode(msg_encoding)
                else:
                    title += sub[0]

            body = ""
            if msg.is_multipart():
                for payload in msg.get_payload():
                    if payload.get_content_type() == "text/plain":
                        body = payload.get_payload()
            else:
                if msg.get_content_type() == "text/plain":
                    body = msg.get_payload()

            print(str(cnt) + " : " + title)
            print(date)
            print(msg_encoding)
            print(body)
            print('')
    finally:
        gmail.close()
        gmail.logout()
        return(cnt)

##メイン
main()

ご教示いただいた内容を元に、bodyの取得処理にBase64のデコード処理を追加したところ、UTF-8のメールを正常に取得できるようになりました。
以下に修正後のソースを載せます(try句を外して整理しています)。

修正後のソース

import imaplib
import email
import dateutil.parser
import base64

def main():
    gmail = imaplib.IMAP4_SSL("imap.gmail.com")
    gmail.login("xxxxxxx@gmail.com","xxxxxxxxxxx")
    gmail.select('Notes')
    typ, [data] = gmail.search(None, "(ALL)")

    #取得したメール一覧の処理
    for num in data.split():
        print(num)

        ### 各メールへの処理 ###
        result, d = gmail.fetch(num, "(RFC822)")
        raw_email = d[0][1]

        #文字コード取得用
        msg = email.message_from_string(raw_email.decode('iso-2022-jp'))
        msg_encoding = email.header.decode_header(msg.get('Subject'))[0][1] or 'iso-2022-jp'

        print(msg_encoding)

        date = dateutil.parser.parse(msg.get('Date')).strftime("%Y/%m/%d %H:%M:%S")

        print(date)

        subject = email.header.decode_header(msg.get('Subject'))
        title = ""
        for sub in subject:
            if isinstance(sub[0], bytes):
                title += sub[0].decode(msg_encoding)
            else:
                title += sub[0]

        print(title)

        body = ""
        if msg.is_multipart():
            for payload in msg.get_payload():
                if payload.get_content_type() == "text/plain":
                    body = payload.get_payload()
                    if msg_encoding == 'utf-8':
                        body = base64.urlsafe_b64decode(body.encode('ASCII')).decode("utf-8")
        else:
            if msg.get_content_type() == "text/plain":
                body = msg.get_payload()
                if msg_encoding == 'utf-8':
                    body = base64.urlsafe_b64decode(body.encode('ASCII')).decode("utf-8")
        print(body)
        print('')
    gmail.close()
    gmail.logout()
    return(0)

main()

以下のコードでContent-Type内のcharsetを取得できました。

追加コード

s = email.header.decode_header(msg.get('Content-Type'))[0]
ss = repr(s[0]).split(';')
if len(ss) > 1:
    c = ss[1].split('charset="')
    if len(c) > 1:
        code = c[1].split('"')
        if len(code) > 1:
            msg_encoding = code[0]