Python でメールを取得 (IMAP)
2023/08/31, last updated 2023/09/01 - ~2 Minutes
Python を使って IMAP サーバからメールを取得する方法。
サンプルコード
以下のリンクのとおり。
IMAP4 の使用例 から転載
import getpass, imaplib
M = imaplib.IMAP4()
M.login(getpass.getuser(), getpass.getpass())
M.select()
typ, data = M.search(None, 'ALL')
for num in data[0].split():
typ, data = M.fetch(num, '(RFC822)')
print('Message %s\n%s\n' % (num, data[0][1]))
M.close()
M.logout()
imaplib search の補足
IMAP4.search(charset, criterion[, ...])
criterion にセットするのは、 RFC3501 section 6.4.4 に記載の search keys と思われる。
search が返すのは、(‘OK’, [b'1303 1302 1301 1300 1298 1297 1296’]) のようなデータ。そのため、data[0].split() のようにしてメッセージ番号をリストにしている。
imaplib sort
sort というメソッドもあり、search 結果を並べかえてくれる。
IMAP4.sort(sort_criteria, charset, search_criterion[, ...])
となっているが、「sort_criteria のリストを丸括弧で囲ったもの」である。sort_criterial は、 RFC5256 3.Additional Commands に記載のものと思われる。
例えば、’(REVERSE DATE)’ などを指定。
imaplib fetch の補足
IMAP4.fetch(message_set, message_parts)
となっている。第2引数は FETCH Command に記載されている。
fetch すると、既読に変わる。
未読のままにしたければ、上の例だと、M.fetch の後ろに
M.store (num, '-FLAGS', '\\Seen')
とすると、未読に戻る。
M.store の引数も RFC を見ないとわからない。
- 第2引数は STORE Command
- 第3引数は Flags Message Attribute
imaplib fetch で取れるメッセージ
('OK', [(b'1303 (FLAGS (\\Seen NonJunk) RFC822 {3714}', b'メールの中身', b')'])
のような感じで返ってくる。
そのため、data[0][1]のようにして本文にアクセスしている。
from を取りたければ、
import email
:
中略
:
msg = email.message_from_bytes(data[0][1])
として変数に代入したなら、
msg['From']
でアクセスできる。
From や Subject など、=?UTF-8?B?〜?= のような形式なっていることがあり、以下のような関数を作って元に戻した。
def decode_header(raw_header):
ret = []
for h, enc in email.header.decode_header(raw_header):
if enc is None:
if isinstance(h, str): # str で返ってくることもある?よくわからん。
ret.append(h)
else:
ret.append(h.decode('ascii'))
else:
ret.append(h.decode('utf-8'))
return ' '.join(ret)
日付は、
import email.utils
:
中略
:
dt = email.utils.parsedate_to_datetime(msg['Date'])
のようにしてやれば、datetime オブジェクトとして取得できる。
応用
定期的にループして、Unseen メールを取得し、前回チェックした時以降のものであれば、slack の #general チャンネルに post する、などとすれば、新規メールを slack で通知できるだろう。(そういう slack アプリはあるので、自分でやるのは、単なる勉強の意味合いしかないが)