Python3.6.0でGmailのUTF-8メールのデコードに失敗
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]