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

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

# 環境
- Python3.2.5
- Gmailとの接続にimaplibを使用
- Windows版で検証

# ログインとラベル指定
接続とラベル指定はシンプルです。

```py3
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](http://tools.ietf.org/html/rfc3501#section-6.4.4)

```py3
    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`を設定しています。
その後また、その文字コードでデコードしているんですがここはもっといい書き方がありそうな気がする…。


```py3
    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()`で確認できます。

```py3
['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']
```

## 差出人/タイトルの取得
差出人を取得します、がここで一苦労。

```py3
        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型になります。
なので、ループ＋型判断で全体を取得しています。

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

```py3
        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ライブラリで差出人名がたまにデコードされない時の対処](http://qiita.com/stkdev/items/33f2bd1d975f93dc4d81)


## 日付/本文の取得
日付の取得とフォーマット変更です。
yyyyMMdd形式にしたい場合、`dateutil`を使うと簡単です。

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

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

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

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

```py3
        #未読に戻す
        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](https://developers.google.com/gmail/imap_extensions)


# 参考
上で出たのも含め
[INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1](http://tools.ietf.org/html/rfc3501)
[Gmail IMAP Extensions](https://developers.google.com/gmail/imap_extensions)
[IMAP4（Internet Mail Access Protocol version 4）～前編](http://www.atmarkit.co.jp/ait/articles/0106/13/news001.html)
[IMAP4（Internet Mail Access Protocol version 4）～後編](http://www.atmarkit.co.jp/ait/articles/0107/03/news002.html)
[email — 電子メールと MIME 処理のためのパッケージ](http://docs.python.jp/3.4/library/email.html)


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