Gmail API を使って監視メールチェックを楽にしたい その4

  • #技術ブログ 

いつの間にやら、猛暑もどこかへ。
2ヶ月も経てばそうなりますよね~、っと、サボり癖が抜けないうえピーです。

さて、今回は「Gmail API を使って監視メールチェックを楽にしたい」シリーズ最終章、条件指定して一括取得したメールをテキストに出力しちゃうです。

お陰様で、ユーザー数も増えて、たくさんの監視結果メールが私のところに届くようになった今日この頃。
Gmailで1件ずつチェックでは、時間がいくらあっても足りなくなってまいりました。
最終的には、一括監視画面を作って、そこで効率的にチェックしたいところですが、まずは対象のメールをすべて同じテキストファイルに出力して、ブラウザで1件ずつ確認する手間を省きたいと思います。

今回も前回までに作成したquickstart.py(詳細はその3をご覧下さい)に関数を追加して、メールの本文をテキストファイルに出力します。
前回同様、GmailAPIのUsers.messagesを参照してみましょう。

なるほど、本文(body)だけ取得できる好都合な関数はGmail APIにはないようです。とほほ。
そうなると、その2でやったのと同じようにrawで取得して、base64デコード、utf-8でstring変換、python標準のemailパッケージで、messageオブジェクトに変換すれば、bodyだけ上手く抜き取れるかな。まずは、messageオブジェクトをdirしてみよ。

Let's Try!!
response = service.users().messages().list(userId=user_id, q=query).execute()
if 'messages' in response:
    for message_id in response['messages']:
        message = service.users().messages().get(userId=user_id, id=message_id['id'], format='raw').execute()
        msg_str = base64.urlsafe_b64decode(message['raw'])
        mime_msg = email.message_from_string(msg_str.decode('UTF-8'))
        print(dir(mime_msg))
        break

dir標準出力結果:
['__bytes__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__dir__', 
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', 
'__init__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__',
'__weakref__', '_charset', '_default_type', '_get_params_preserve', '_headers', '_payload', '_unixfrom',
'add_header', 'as_bytes', 'as_string', 'attach', 'defects', 'del_param', 'epilogue', 'get', 'get_all',
'get_boundary', 'get_charset', 'get_charsets', 'get_content_charset', 'get_content_disposition',
'get_content_maintype', 'get_content_subtype', 'get_content_type', 'get_default_type', 'get_filename',
'get_param', 'get_params', 'get_payload', 'get_unixfrom', 'is_multipart', 'items', 'keys', 'policy',
'preamble', 'raw_items', 'replace_header', 'set_boundary', 'set_charset', 'set_default_type', 'set_param',
'set_payload', 'set_raw', 'set_type', 'set_unixfrom', 'values', 'walk']

あびゃー、bodyだけ抜ける関数無いし、、、とほほ。

うーん、しょうがないので、力技でメール全量からヘッダーを除去しよ。
折角なので、メールの何件目を出力したか分かるようにしよっと。
そんな感じで最終形がこちら。

def outMessages(service, user_id, outfilename, query=''):
    if os.path.exists(outfilename):
        os.remove(outfilename)
    response = service.users().messages().list(userId=user_id, q=query).execute()
    outfile = open(outfilename,"a", encoding="utf-8", newline="\n")
    count = 0
    if 'messages' in response:
        for message_id in response['messages']:
            count+=1
            message = service.users().messages().get(userId=user_id, id=message_id['id'], format='raw').execute()
            msg_str = base64.urlsafe_b64decode(message['raw'])
            mime_msg = email.message_from_string(msg_str.decode('UTF-8'))
            # メールヘッダーを出力しない
            # 方式1 メモリを食う
#            linesWork = mime_msg.as_string().splitlines()
#            lines = mime_msg.as_string().splitlines()
#            for line in linesWork:
#                lines.remove(line)
#                if -1 < line.find(""):
#                    break
#            outfile.write("\n".join(lines))
            # 方式2 ファイルIOを使いそう 何件目のメールを出力したかも出力したいのでこちらを採用
            outfile.write("count = " + str(count) + "\n")
            lines = mime_msg.as_string().splitlines()
            start = False
            # ヘッダーの最終行に含まれる文字列が現れるまでファイルに出力しない
            for line in lines:                
                if start :
                    outfile.write(line + "\n")          
                if not start and -1 < line.find("ヘッダーの最終行に含まれる文字列"):
                    start = True

    while 'nextPageToken' in response:
        page_token = response['nextPageToken']
        response = service.users().messages().list(userId=user_id, q=query, pageToken=page_token).execute()
        for message_id in response['messages']:
            count+=1
            message = service.users().messages().get(userId=user_id, id=message_id['id'], format='raw').execute()
            msg_str = base64.urlsafe_b64decode(message['raw'])
            mime_msg = email.message_from_string(msg_str.decode('UTF-8'))
#            outfile.write(mime_msg.as_string())
            # メールヘッダーを出力しない
            # 方式1 メモリを食う
#            linesWork = mime_msg.as_string().splitlines()
#            lines = mime_msg.as_string().splitlines()
#            for line in linesWork:
#                lines.remove(line)
#                if -1 < line.find(""):
#                    break
#            outfile.write("\n".join(lines))
            # 方式2 ファイルIOを使いそう 何件目のメールを出力したかも出力したいのでこちらを採用
            outfile.write("count = " + str(count) + "\n")
            lines = mime_msg.as_string().splitlines()
            # ヘッダーの最終行に含まれる文字列が現れるまでファイルに出力しない
            start = False
            for line in lines:                
                if start :
                    outfile.write(line + "\n")                
                if not start and -1 < line.find("ヘッダーの最終行に含まれる文字列"):
                    start = True
    outfile.close()

とりあえず、僕の個人PCで実行するものだからこのレベルで大丈夫っしょ!!
P.S.
実際、チェック時間は半分以下に!!いぇい!
じゃあまたねー。