📨
PythonでMS Outlookのmsgファイルを読む(msg-parser)
はじめに
Outlookで受信したメールのうち、特定のメーリングリスト宛に誰からどのくらいの頻度でメールが来ているか調べて欲しいと言われた。
調査用データとして、過去数年分のメールデータ(Outlook .msg形式)数千件が提供された。
Outlookに取り込んでルールで振り分けを試みたが、メーリングリストに投稿したユーザーで振り分けることができないことが分かった。
(やり方が悪いのかもしれないが…)
msgファイルを1通ごと開いて調べるのは非現実的なので、Pythonの力を借りて自動化した。
コード
実証実験コード
「.msg」形式を読むためのパッケージは複数存在するが、今回はmsg-parserを用いた。
test.py
import json
from msg_parser import MsOxMessage
msg = MsOxMessage('./test.msg')
print(json.dumps(msg.header_dict))
output
{
"Return-path": "<ml-bounces@example.jp>",
"Envelope-to": "kuma@example.jp",
"Delivery-date": "Fri, 15 Aug 2014 15:57:01 +0900",
"Received": "from localhost ([127.0.0.1] helo=mail.example.jp)\r\n\tby mail.example.jp with esmtp (Exim 4.69)\r\n\t(envelope-from <ml-bounces@example.jp>)\r\n\tid 1XIBRa-0007sf-Ew; Fri, 15 Aug 2014 15:56:58 +0900",
"From": [
"=?ISO-2022-JP?B?GyRCJWYhPCU2ITwbKEI=?= <user@example.jp>"
],
"Message-Id": "<xxxxxxxx-xxxxx-xxxxx-xxxx-xxxxx@example.jp>",
"Date": "Fri, 15 Aug 2014 15:56:59 +0900",
"To": [
"=?ISO-2022-JP?B?GyRCJWEhPCVqJXMlMCVqJTklSBsoQg==?= <ml@example.jp>"
],
"Mime-Version": "1.0 (1.0)",
"Subject": "[ml 373] =?ISO-2022-JP?B?GyRCOiM3biRORGpOYzJxNUQkSyREJCQkRhsoQg==?=",
"Content-Type": "multipart/mixed; boundary=\"===============7918749803048529676==\"",
"Sender": "ml@example.jp",
"Errors-To": "ml-bounces@example.jp",
"CC": [],
"BCC": [],
"Reply-To": []
}
ヘッダー情報をdict
で取れてくることが分かった。
また、本来の送信者(上記サンプルで言うuser@example.jp
)も取れてきそうということが判明した。これは使える。
また、ヘッダー情報以外にも、送信日や本文を取得するメソッドが別途用意されている。
(詳しくはドキュメントを参照されたい)
本番コード
ターゲットディレクトリ内に存在する.msg
ファイルを走査し、「送信日」「送信者」を取得、集計できるよう準備をする。
extract.py
import glob
import re
from datetime import date, datetime
from msg_parser import MsOxMessage
files = glob.glob("./target/*.msg")
for file in files:
msg = MsOxMessage(file)
from_raw = msg.header_dict['From'][0]
from_str=re.search(r'<(.+@.+)>', from_raw)[1]
sent_date_raw = msg.sent_date
sent_date = datetime.strptime(sent_date_raw, '%a, %d %b %Y %H:%M:%S %z')
print (sent_date.strftime('%Y%m') + "," + from_str)
output
201809,user_k1@example.jp
201809,user_k1@s.example.jp
201809,user_k2@example.jp
201809,user_i1@example.jp
注意点
-
ヘッダの「From」は配列なので、1件目を取ってくる。
そもそもメールに「From」が複数存在するパターンってあるの?と思うが、パッケージの仕様のようなので従う。 -
「From」にセットされてくる値はメールアドレスだけではないので、正規表現でメールアドレスだけ抽出
=?ISO-2022-JP?B?GyRCJWEhPCVqJXMlMCVqJTklSBsoQg==?= <ml@example.jp>
、メーリングリスト <ml@example.jp>
といった形式で格納されている。
このまま使ってもよいが、文字列部分はメーラ側で自由に変更できてしまうので、<>
で囲まれたメールアドレス部だけを使うことにした。 -
「sent_date」はそのままでは日付時刻としてパースできない。
これはメールサーバ側の仕様に合わせて書式を指定してパースしてやる必要がある。
今回はFri, 15 Aug 2014 15:56:59 +0900
のような形式で取得できたので、
%a, %d %b %Y %H:%M:%S %z
と書式を決め打ちした。%c
でのパースは失敗した。
後処理
雑にCSV形式で表示しただけなので、ファイルにリダイレクトしてやる。
python extract.py > extract.csv
しかる後、Excelにインポートしてピボットテーブルで件数集計を実施した。
done!
Discussion