とりあえずコード書けよ

技術的なことの備忘録。

Outlookのメールをcsvに変換する方法

めちゃニッチな話だけど、調べても特に情報が出て来なかったので、万が一誰かの役に立つかもしれないから残しておく。
ちなみにoffice for macの話です。

事のいきさつとしては、定期購入で商品を買っているんだけど、商品を発送しましたとかって定型の通知メールが色々届くんですが、いままでいつ何が送られて来たのかをデータとして管理したかった。
(残念ながらCSV出力等の機能はそちらにはなかった。)

なので、メールだけが頼りなんだけど、それをさすがに一個一個調べていってcsvで管理とかは無駄無駄無駄無駄ァ!って感じなので避けたかったんですよね。

まず考えたのはメールソフトでCSV出力できないの?だった。 で、調べたらどうやらOutlookがいけるらしいぞ、というのを下をみて思ったわけですよ。
jp.tipsandtricks.tech

さすがMicrosoft、俺たちにできない事を平然とやってのけるっ!と思って、早速Outlookを導入。
(ちなみに、わざわざOffice365を契約した(1ヶ月無料なので、まだダメージはない))

で、いざツール→エクスポートって進んでみてもないんですよね。CSV出力なんてものは。

そもそも↑のサイトもOffice for macは.olmしかないよ!って書いてあるんですよね。
マニュアルはよく読もう。

.olmをハックする

仕方がないので、.olmというこの謎のファイルをどうにかしようと思ったわけです。
で、olm to csv とかで調べてみると、一応↓のようにソフト自体は存在することが分かった。

www.adviksoft.com

ためしに使ってみたものの、フリーだと25件しか解凍できないとのことで、ライセンスも$39だし、ピンポイントにしか需要がないのでそこに$39も払うのもなーという感じ。
そこでふと、エンジニアの血が騒いでしまった。
いうてエンジニアの端くれなんだし、なんか面白そうだし、いじってみよと。
大人になってから夏休み終了間際(日付的に。)に自由研究を嬉々としてやることになるとは思わなかった。

.olmとはなにものか

まず.olm自体がなんなのかよく分からなかったので、とりあえずvimで開いてみた(雑)。 まぁ案の定読めなかったんですけど、エンコードの問題かなーとか思ったんで、エンコード調べよ!って思ったらfileに--mimeっていう素晴らしいオプションがあるんですね。全然知らんかった。

# --mimeでエンコードも出力される。
file --mime hoge.olm
→hoge.olm:: application/zip; charset=binary

ってなったんで、いや、っていうかそもそもお前zipだったんか、っていうところでエンコードどうこうよりも、とりあえず解凍しようと思ったわけです。

How to Unzip

普段The Unarchiver使ってるんで(そもそも使う機会ほぼないですが)、突っ込んでみたら「ファイルが壊れています」でNG。
次はアーカイブユーティリティに突っ込んでみてもこれまたNG。
で、最終兵器unzipに突っ込んだんですけど、まさかのなんとかしてくれた。unzip最強説。

unzip hoge.olm
# Illegal byte sequenceとかでちょこちょこエラー出ますけどメッセージの取り出し自体は問題はなさげ

で、いざ解凍してみるとこんなディレクトリ構成になりました。

Accounts  
∟hoge@xxx.ne.jp  
 ∟com.microsoft.__Messages  
  ∟INBOX  
   ∟message_00000.xml
   ∟message_00001.xml
   ∟...大量のxml

メッセージはそれぞれxmlことが分かりました。
ここまできたらもう勝ったも同然。

xmlをパースする

とりあえずディレクトリ構成を解凍した状態から若干変更します。
適当な作業ディレクトリ ∟INBOX
| ∟message_00000.xml
| ∟message_00000.xml

xml_to_csv.rb 以下xml_to_csv.rbの中身。

# frozen_string_literal: true

# REXML::Documentを使ってパースしていく
require 'rexml/document'
require 'csv'

# CSVファイルの生成
CSV.open('./output.csv', 'w+', col_sep: "\t") do |csv|
  # ヘッダ行の設定
  csv << %w[subject sent_at body]
  # Dir.glob使って、解凍したxmlファイル名を読み込み
  Dir.glob('./**/*.xml').each do |xml_file|
    # 読み込んだxml_file名からfileを開く
    xml_doc = REXML::Document.new(File.new(xml_file))
    # 件名の読み取り
    subject = xml_doc.elements['emails/email/OPFMessageCopySubject'].text
    # 送信日時の読み取り
    sent_at = xml_doc.elements['emails/email/OPFMessageCopySentTime'].text
    # 本文の読み取り
    body = xml_doc.elements['emails/email/OPFMessageCopyBody'].text
  end
end

あとは本文(↑でいうbodyに格納されてる内容)から正規表現等々を駆使して自分で欲しい情報を取り出していけばお好みのメールcsvが作れます。

まとめ

  • .olmはunzipで解凍できる。
  • メッセージはxmlでできている。
  • あとはxmlをパースしましょう。

めでたしめでたし。