Edited at

Python3でGmailからメール取得とラベル付け

More than 3 years have passed since last update.

Gmailから特定のラベルが付いたメールを引っ張ってきたかったのでPythonで作ってみたメモです。


環境


  • Python3.2.5

  • Gmailとの接続にimaplibを使用

  • Windows版で検証


ログインとラベル指定

接続とラベル指定はシンプルです。

import imaplib, re, email, six, dateutil.parser

email_default_encoding = 'iso-2022-jp'

def main():
gmail = imaplib.IMAP4_SSL("imap.gmail.com")
gmail.login("user","password")
gmail.select('INBOX') #受信ボックスを指定する
gmail.select('register') #ラベルを指定する


メールの取得

.search()でメールを取得します。

ALLを指定するとすべて、UNSEENで未読のものが取得できます。

ほかに設定できるものはIMAP4のマニュアル見るのが確実。英語ですが。

INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1

    typ, [data] = gmail.search(None, "(UNSEEN)")

#typ, [data] = gmail.search(None, "(ALL)")

#確認
if typ == "OK":
if data != '':
print("New Mail")
else:
print("Non")

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

#お片付け
gmail.close()
gmail.logout()

中段の部分は受信できたかどうかの確認しているだけです。

そのあと各メールへの処理を書いて、終了の流れです。

次からメール処理部分で、差出人・タイトル・本文の取得を行います。


メール内容の取得


文字コード取得とパース

.search()で取得できるのは対象のメールのidのみなので.fetch()で指定したidのメール全体にアクセスできるようにします。

最初にメールの文字コードを取得する必要があります。

email.message_from_stringを使いメールをパースしたあとタイトル部分にアクセスして、文字コードが指定されていればそれを、なければデフォルト値iso2022-jpを設定しています。

その後また、その文字コードでデコードしているんですがここはもっといい書き方がありそうな気がする…。

    for num in data.split():

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

#文字コード取得用
msg = email.message_from_string(raw_email.decode('utf-8'))
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))

print(msg.keys())

ここで取得できる項目はmsg.keys()で確認できます。

['Delivered-To', 'Received', 'X-Received', 'Return-Path', 'Received', 'Received-SPF', 'Authentication-Results', 'DKIM-Signature', 'Subject', 'From', 'To', 'Errors-To', 'MIME-Version', 'Date', 'X-Mailer', 'X-Priority', 'Content-Type', 'Message-ID', 'X-Antivirus', 'X-Antivirus-Status']


差出人/タイトルの取得

差出人を取得します、がここで一苦労。

        fromObj = email.header.decode_header(msg.get('From'))

addr = ""
for f in fromObj:
if isinstance(f[0],bytes):
addr += f[0].decode(msg_encoding)
else:
addr += f[0]
print(addr)

"差出人<xxxx@xxx.jp>"のような表記のものをパースすると

fromObj[0][0] : b'xxxxxxxxx'  ・・・"差出人"がエンコードされたもの

fromObj[0][1] : 'iso-2022-jp' ・・・文字コード
fromObj[1][0] : b'<xxxx@xxx.jp>' ・・・アドレス部分
fromObj[1][1] : None ・・・英数字のみのため文字コードなし

という形式で取得できるようです。

また、日本語表記がない場合"Sashidashi<xxxx@xxx.jp>"のような場合は分解されず

fromObj[0][0] : 'Sashidashi<xxxx@xxx.jp>'

fromObj[0][1] : None

という形でstr型になります。

なので、ループ+型判断で全体を取得しています。

タイトルも同じようにするとうまくいきました。

        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)

また、差出人部分は一部のメールで日本語が入っている場合にデコードがうまくできない場合がありました。別記事でまとめます。

※追記

まとめました。⇒Pythonのemailライブラリで差出人名がたまにデコードされない時の対処


日付/本文の取得

日付の取得とフォーマット変更です。

yyyyMMdd形式にしたい場合、dateutilを使うと簡単です。

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

print(date)

本文の取得はまた分岐が入っています。

.get_payload()で取得できますが、html形式で送られてきているメールの場合textもhtmlも一緒に取得されてくるので、text/plainのものを取り出しています。

        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()


ラベルの設定と削除

ラベルの設定と削除のやり方がなかなかわからず苦労しました。

        #未読に戻す

gmail.store(num, '-FLAGS','\\SEEN')

#ラベルを追加
gmail.store(num, '+X-GM-LABELS','added')
#ラベルを削除
gmail.store(num, '-X-GM-LABELS','added')

"X-GM-LABELS"の指定でできるみたいです。追加なら"+"、削除なら"-"。

情報元:Gmail IMAP Extensions


参考

上で出たのも含め

INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1

Gmail IMAP Extensions

IMAP4(Internet Mail Access Protocol version 4)~前編

IMAP4(Internet Mail Access Protocol version 4)~後編

email — 電子メールと MIME 処理のためのパッケージ

いやー、imap4とかもちゃんと理解しとかないと難しいなぁ。