IMAP4でGMailの不要メールを削除する
プログラムに必要な情報
IMAPのライブラリ
IMAPの接続サーバー情報
IMAPの接続用パスワード
https://gyazo.com/f717b2804aef37cb8b2fbed8aeec6553
さっそくimaplibの公式ドキュメントを読みながらコードを書いてみた
code:mail.py
import imaplib
with imaplib.IMAP4_SSL(host='imap.gmail.com') as m:
typ, data = m.noop()
print(typ, data)
typ, data = m.login('<shimizukawa-mailaddress>@gmail.com', '<generatedpassword>')
print(typ, data)
if typ != 'OK':
raise RuntimeError(data)
typ, data = m.select(readonly=True)
print(typ, data)
if typ != 'OK':
raise RuntimeError(data)
typ, data = m.search(None, '(UNSEEN)')
print(typ, data)
if typ != 'OK':
raise RuntimeError(data)
実行結果:
code:shell
$ python3 mail.py
デフォルトのメールボックスに2件のメール(番号 68, 69)があるというところまで分かった。
では、削除対象メールを検索してみる。
after:2018/5/1 before:2018/6/1 from:(Email Bounces) subject:AWS
このような検索をIMAPのプロトコルで行うには、 RFC 3501 の書式に合わせて書く必要がある。 code:mail.py
m.search(None, 'SINCE 1-May-2018 BEFORE 1-JUN-2018 FROM "Email Bounces" SUBJECT "AWS"')
けど、面倒なのでGmailの検索文字列を使いたい。
GMailの拡張コマンドを使うと、GMailの検索ボックスと同じ書式で検索できる。
code:mail.py
m.search(None, 'X-GM-RAW "after:2018/5/1 before:2018/6/1 from:(Email Bounces) subject:AWS"')
あとは検索結果で取得できた各メールを削除すればよい。
code:maildelete.py
import imaplib
from email.parser import BytesHeaderParser
from email.utils import parsedate
import time
from datetime import datetime, date, timedelta
from itertools import product
with imaplib.IMAP4_SSL(host='imap.gmail.com') as m:
typ, data = m.login('<shimizukawa-mailaddress>@gmail.com', '<generatedpassword>')
if typ != 'OK':
raise RuntimeError(data)
typ, data = m.list()
if typ != 'OK':
raise RuntimeError(data)
box = 'INBOX'
for d in data:
d = d.decode()
ext, name = d.split('"/"')
ext = ext.strip().strip('(').strip(')').split()
name = name.strip().strip('"')
if '¥All' in ext:
box = name
print('Select box:', box)
typ, data = m.select(box)
if typ != 'OK':
raise RuntimeError(data)
for year,month in product(range(2018,2010,-1), range(12,0,-1)):
ds = date(year, month, 1)
de = ds + timedelta(days=31)
typ, data = m.uid(
'search', None,
'X-GM-RAW "after:{0:%Y/%m/%d} before:{1:%Y/%m/%d} from:(Email Bounces) subject:AWS"'.format(ds, de))
if typ != 'OK':
raise RuntimeError(data)
uids = data0.decode().split() hparser = BytesHeaderParser()
for uid in uids:
typ, data = m.uid('fetch',uid,"(BODYHEADER)") msg = hparser.parsebytes(data01) dt = datetime.fromtimestamp(time.mktime(parsedate(msg'Date'))) print('del "{dt:%Y/%m/%d %H:%M:%S}, {msgFrom}", "{msgSubject}"'.format(msg=msg, dt=dt)) typ, data = m.uid('STORE', uid, '+FLAGS', r'(¥Deleted)')
m.expunge()
実行結果がこちら
code:bash
$ python3 maildelete.py
<shimizukawa-mailaddress>@gmail.com authenticated (Success)
Select box: Gmail/&MFkweTBmMG4w4TD8MOs- del "2018/04/30 15:03:14, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:03:25, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:03:29, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:04:17, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:04:22, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:04:28, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:04:44, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:04:47, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:04:56, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
del "2018/04/30 15:04:59, Email Bounces <no-reply@sns.amazonaws.com>", "AWS Notification Message"
...
実際にのコードにはリトライ機構を付けたので、接続エラーが出たらちょっと休憩して再実行する。
...
6時間実行した結果
削除できたメールは1万件
期間は2018/5/1~5/9の10日分
速度: 2.16秒/1通削除
おそい!(手動で行うと2時間で8ヶ月分くらい削除できる)
速度向上案
1通毎にheaderを取得しているのをやめる(0.4秒/1通 くらい早くなりそう)
複数件まとめて削除する方法を探す(あるのか不明)
とかやってたら、提案が降ってきた。Twitterすごい。
Gmail APIを使うとできそうですけど、要件に合わないでしょうか?
https://gyazo.com/3cf9cd16686258a5e5af7beda4b0ef02