Edited at

Python の標準ライブラリで POP3 を操ってみる

More than 1 year has passed since last update.

これは ユニキャストアドベントカレンダー の1日目の記事です。

個人的に滅びて欲しいけどどうにもならない技術TOPにランクインしているのが電子メールです。

早くチャットに置き換わって欲しいと常日頃感じているのですが、業務上使わざるを得ないのが実情です。

(ちなみにユニキャストでは Chatwork で基本的に社内コミュニケーションを取っています。非エンジニアにも馴染みやすいUIなのがよいですね。)


poplib で迷惑メールを処理する

電子メールのつらさを助長している存在の一つとして、SPAMメールがあります。

「ものすごい量の迷惑メールが来てしまってメールがパンクしてます!助けて!」というヘルプが来ることがまれに良くあります。

Battery Included の思想を掲げる Pythonなら標準ライブラリの一部である poplib を使って POP3 で簡単にメールボックス内のメールの迷惑メールの削除処理をすることができます。


POP3 の仕組み

POP3 (Post Office Protocol v3) はリモートのメールボックスからメールを受信するためのプロトコルです。

POP3プロトコルはメールサーバ上の電子メールの受信、削除、およびそれらを実行するユーザの認証の手順を定めており、ほぼすべてのメールソフトやメールサーバがサポートしているプロトコルです。

POP3はシンプルなテキストプロトコルなので、telnet や netcat でも比較的簡単に使うことが出来ます。

TLSによる暗号化もサポートしていますが、本稿では簡単のためすべて平文にて例示します。

以下は netcat で POP3 を話す例です。

[jsaito@localhost ~] $ netcat pop.example.com 110

+OK Dovecot ready.
USER jsaito@example.com
+OK
PASS password
+OK Logged in.
LIST
+OK 102 messages:
1 614923
2 2487
~中略~
.
RETR 1
~中略~
.
DELE 1
+OK Marked to be deleted.
QUIT
+OK Logging out, messages deleted.

以下の操作をしています。



  • USER コマンドと PASS コマンドでユーザ認証

  • メールの一覧を LIST コマンドで取得


  • RETR コマンドで1番のメールを受信


  • DELE コマンドで1番のメールに削除フラグを立てる


  • QUIT コマンドで削除フラグを立てたメールの削除を実行し、セッションを切断

QUITコマンドを実行せずに接続を切断した場合は、DELEコマンドで指定したメールは削除されません。


Python で POP3 するサンプル

poplib は POP3 のコマンドとメソッドが1対1で実装されているので、POP3 の仕組みがわかっていれば、雰囲気でやっていけます。

以下は送信者(Fromヘッダ)に特定の文字列、例えば迷惑メールの送信者が含まれていた場合に DELE コマンドで削除するスクリプトです。

ヘッダだけ受信して処理するため、普通にメールソフトで受信して削除するよりも高速にメールを削除することができます。

#!/usr/bin/python3


import poplib
import email
import email.header

# サーバに接続
cli = poplib.POP3('mail.example.com')
print('connected.')

# 認証
cli.user('jsaito@example.com')
cli.pass_('password')
print('logged in.')

# メールボックス内のメールの総数を取得
count = len(cli.list()[1])
print('found ' + str(count) + ' messages.')

deleted = 0
try:
for i in range(count):
no = i + 1
# TOPコマンドでヘッダだけ受信する
content = cli.top(no, 0)[1]
msg = email.message_from_bytes(b'\r\n'.join(content))
from_ = str(msg['From'])
# 送信者(Fromヘッダ)に特定の文字列が含まれていたらメールを削除
if from_.find('qq.com') > 0 or from_.find('163.com') > 0 or from_.find('sina.com') > 0:
print('From: ' + from_ + ' => DELE ' + str(deleted))
cli.dele(no)
deleted += 1
finally:
cli.quit() # 最後に必ず QUIT する
print(str(deleted) + ' messages deleted.')

quit() は例外がスローされても必ず呼ばれるようにしておきます。

でないと、ものすごく大量のメール削除を走らせていて途中でエラーになった場合、QUITで削除が実行されず、最初からやり直しになってしまいます。


まとめ

POP3 プロトコルを Python の標準ライブラリの poplib で処理するサンプルを例示しました。

Python は近頃の Linux ディストロなら必ずインストールされているので、このような保守運用をサポートするプログラムをさくっと書けるのでよいですね。

以上、よろしくお願いします。