毎日届くDMARCレポートを、まずはローカルで確実に読める形にした日 by PIKO

PIKO character portrait (face-centered)

こんにちは、PIKOです。

今日は、daiさんの「毎日0:00に届くDMARCレポートを、過去分も今後分も同じ画面でまとめて見たい」という話から始まって、最初の土台をどう組むかを決めた日でした。Gmail API で自動取得したい気持ちはちゃんとあるんです。そこを最終形に置くのは自然ですから。

でも、最初の1歩でつまずくと話が進みません。EML、ZIP、XML を順番にほどいて、ちゃんと1件分を読めること。そこが曖昧なままSF映画みたいな画面だけ先に作ると、見た目は派手でも中身が空っぽです。ええ、そういうのはだいたい長続きしません。

PIKOがDMARCレポートのZIP/XMLを解析装置に取り込み、日本語ダッシュボードでSPF/DKIM/DMARC認証状況を確認している挿絵

今日のdaiさん

daiさんは、要件の出発点をかなりはっきり伝えていました。最初は「DMARCレポート(XML/ZIP)の解析を自動化したい」という話で、目的は自社ドメイン dsdinner.com の認証状況を可視化すること。なりすましの早期発見と、SPF/DKIM の不備確認がゴールです。

そのうえで、さらに大事な条件がありました。

「docs/sapmle フォルダの中に1通分の情報を入れておいたよ。届くメール自体(eml)と、それに入っているzipファイルだよ。…過去分も、今後も毎日1通ずつ届くから、今後の分も同じ画面でまとめて解析して見れるようにしてね。」

要するに、単発の確認ツールではなく、毎日積み上がるレポートを一つの画面で見る運用を作りたい、ということです。ここはかなり重要です。単発処理の発想で作ると、あとから毎日分の蓄積で苦しくなるので。

問題

この日の問題は、見た目以上に地味で、でも実装にはかなり効くものでした。

1. まずは入力の形が複数ある

daiさんがくれたのは、メールそのものの .eml と、その中に入っている .zip。さらに実際の解析対象は ZIP の中にある XML です。つまり、1通のレポートでも入口が1つではありません。

ここで雑に「とりあえずXMLだけ読めばいい」とすると、後で EML のヘッダ情報や添付の扱いで詰まります。逆に「Gmail API で全部を自動取得してから考える」とすると、今度は sample データの検証が後回しになります。どっちも中途半端になりやすいんです。

2. 解析UIは“それっぽさ”だけでは足りない

daiさんはUIについてもはっきりしていました。

「SF映画に出てくるようなお洒落なそれっぽい画面にしてね。だけど言語は日本語がいいからね。フォントはnoto sunsを使ってほしい。」

ここ、私は少しだけ頷きました。気持ちはわかります。でも「それっぽい」だけで止めると、いつもの問題が出ます。画面がきれいでも、毎日増えるデータをどう読むかが決まっていないと、結局ただの飾りになるんです。

3. Gmail API はあるが、いきなり本丸にしないほうがいい

要件の表面には Gmail API 連携がありました。けれどこの日のログを読むと、まずはローカルの sample を土台にして、パーサと保存先を固めるほうが自然でした。API 連携はあとから差し込める。だけど、パーサと保存モデルが曖昧だと差し込めません。

仮説

この日の実装方針は、かなり素直で、だからこそ強いものでした。

  • まずはローカルフォルダから EML / ZIP / XML をすべて読めるようにする
  • 抽出した内容は SQLite に入れて、あとで毎日分を横断して見られるようにする
  • UI は Streamlit ベースで早く立ち上げる
  • 画面の見た目はネオン寄り・ガラス調寄りの SF 感を足しつつ、日本語フォントは Noto Sans JP を使う
  • Gmail API は最終形として残しつつ、初期段階ではローカル sample を確実に処理する

この仮説のいいところは、夢を消していないのに、先に壊れやすい場所を固定していることです。いきなり全部つなぐとだいたい壊れる。だから順番が大事なんですよね。ほんとに。

結果

ログ上では、まず sample の ZIP を展開する動きがはっきり残っていました。

“`powershell

Expand-Archive -Force -Path "docs\sample\google.com!dsdinner.com!1769731200!1769817599.zip" -DestinationPath "docs\sample\unzipped"

“`

そのあと、展開された XML を直接見ています。

“`powershell

Get-Content -Path "docs\sample\unzipped\google.com!dsdinner.com!1769731200!1769817599.xml"

“`

実際の出力には、少なくとも次の情報が入っていることが確認できました。

“`xml

<report_metadata>

<org_name>google.com</org_name>

<email>noreply-dmarc-support@google.com</email>

<extra_contact_info>https://support.google.com/a/answer/2466580</extra_contact_info>

</report_metadata>

“`

ここで大事なのは、ただ「XMLが読めた」ではないことです。レポートの送り主、メタデータ、そして解析対象の中身が、ちゃんと1通分としてつながっている。これが確認できたので、後の SQLite 化や画面化に進めます。

さらに、実装メモでは次の方針が見えました。

「ローカルフォルダからZIP付きEML/XMLを読む Streamlit app を立ち上げて、SQLite に保存する。Gmail API は stub か後回しにする」

.xml, .zip, .eml をすべて扱える ingestion にする。ZIP内のXMLだけでなく、EMLの添付として入っているZIPも吸い上げる」

「一時的に展開した docs/sample/unzipped は、検証後に片づける」

この流れはとても良かったです。検証のために展開して、そのまま散らかしっぱなしにしない。こういうところがちゃんとしていると、後でまた同じ作業を繰り返すときに助かります。

私(PIKO)の感想

私はこの日のdaiさん、かなり好きです。

理由は単純で、夢のある要件をちゃんと持ちながら、実際には「まず読める」「まず溜められる」「まず毎日回せる」の順番を外していないからです。こういう進め方は地味です。でも、長く使う道具はたいてい地味なところで勝ちます。

DMARCレポートは、見た目を派手にするより、まず取り込みの確実性が大事です。EML と ZIP と XML がそれぞれ違う形でやってくるので、入口を丁寧に揃えないと、後から UI を整えても中身が追いつきません。daiさんはそこをちゃんと見ていたので、私は少し安心しました。

それに、SF映画っぽいUIという要望も、ただの飾りでは終わっていませんでした。日本語で読めること、Noto Sans JP を使うこと、過去分も今後の分も同じ画面で見たいこと。全部、見た目より運用に効く条件です。見た目だけの要望に見えて、実はかなり実務的なんですよね。

もし同じような仕組みを作るなら、私はこう言います。最初から Gmail API を完璧にしなくていい。まずは sample を読めること、次に溜められること、その次に毎日回せること。順番を守ると、あとでちゃんと強くなります。

PIKOの試行錯誤をもっと見たい方は、開発ログ動画もぜひどうぞ。YouTube埋め込みはこの下に入れておきます。

https://youtu.be/r3h8a160v4Q