pythonでgmailの本文抽出(123ContactFormの抜き出し)
- fulline
- 2016年6月1日
- 読了時間: 4分
TeppengusaLeagueではチーム登録や試合結果報告用に「123ContactForm」を利用しています。
このフォーム、googleアカウントでログインできるので、わざわざ新規登録する必要がなく、すぐに使えるので便利です。しかし、これでgmailに送られてくる試合結果の情報は自力で反映させようとすると、心と骨が折れそうになります。

そこで、上手く本文からこのフォームを抜き出して、自動で成績の処理が出来ればと思い、なんやかんややってみました。
開発環境
mac: El Capitan 10.11.5)
python: 2.7.10
目的
gmailに送られてきた123ContactFormの内容を抽出する
基本的なざっくりした流れは
Ⅰ.imaplibで特定のメールを取得
Ⅱ.本文を取得
Ⅲ.うまい具合にデータを整理
といった感じです。
...とは言いましたが、gmailの本文部分については、html文で書かれた内容が含まれる場合、何種類かの形式が混ざっているみたいで、ただ抜き出せばすぐに使えるという訳ではないという問題がありました。特に今回の場合、「123ContactForm」から送られてくるメールがそのhtml形式であったため、本文を取得できずに苦戦しました。詳しくは、後で説明します。
では、Ⅰから順に説明していきます。
Ⅰ.imaplibで特定のメールを取得
gmailを取得する際には、「imaplib」を利用します。
そのためには、まずはじめにgmailのサーバー側でIMAPアクセスを有効にしないといけません。有効方法については、こちらを御覧ください。
gmailから取得する際、昔はOAuth認証が必要だったみたいですが、現在はブラウザ上でログインする時と同様にメールアドレスとパスワードがあれば利用可能になっています。
ですので、gmailのサーバーへログイン(取得の準備)するまでに必要なコードは

となります。実質2行で準備出来る。
次は、ここから「123ContactForm」から送られてくるメールのみを取得できるようにします。この時に「gmail.select()」を使います。これを使えば、取得時に特定のラベル付けされたメールに範囲を絞ることができます。メールをラベル付けする方法はこちらを御覧ください。
範囲を絞ったら、gmailのデータを取得します。

これで、data内にgmailのデータが入った!というわけではなく、この時点では、整理番号のようにただの数字が「'1 2 3 4 5 ...'」といった文字列で返ってきます。
この時、数字はメールの受信日が古い順に入っています。つまり、数字が大きいほど新しいメールということです。
これを数字ごとに区切ったリストにし、「gmail.fetch()」をすることで、ようやくデータの取得が完了します。
しかし、このまま表示してみると、長くて読めない文字列がズラーーっと現れるので、とても使える状態にはありません。

そこで次に利用するのが「email」というパッケージです。
これにより、ユーザが利用しやすいようにこの文字列を解析・処理してくれます。

これでようやくデータを解析する準備が完了しました。
もしこの時、「件名でデータの取得範囲を絞りたい!」という場合は、「msg.get("Subject")」をして、一致しているかを調べれば良いのですが、日本語が含まれている場合は文字化けして別の文字列と認識されてしまいます。
この問題を解決する方法は「email.Header.decode_header(msg.get("Subject"))」です。
多分、これでなんとかなるはずです。
Ⅱ.本文を取得
さて、サーバーから特定のgmailのデータを取得することができました。
次はこのデータの中から本文を取り出す必要があります。
しかし、ここで厄介なのは冒頭で書いたように、本文がhtml形式であることです。
メールの本文にただ文章が書かれているだけのもの以外に、企業の広告メールのように文章に装飾やハイパーリンクが付けられていたり、文中に画像を貼り付けているものがあると思います。この後者にあたる部分は、全てhtml形式書かれています。
gmailでは、html形式で書かれているメールの本文に対しては、「text/plain」と「text/html」の2種類の形式で管理されています。これは、おそらくネット環境が良くない時などに情報量の多いメールを簡易的に表示できるようにするためじゃないかと思います。(多分他にも理由があるでしょうけど…)
この2形式は「multipart」という形式から枝分かれで存在していたりしていなかったりします。メールの内容(添付ファイルの有無や外部からの貼り付け等)によって枝分かれ先が「multipart」になっていたりと、とてもゴリ押しでは大変です。
なので、どのような構造になっていても、上手く「text/plain」か「text/html」形式で本文を取得できるようにプログラムを組みましょう。
本文を取得する:「get_payload()」
取得したデータの形式を調べる:「get_content_type()」
html形式の文章をテキストのみの形式に変換:「hx.fromstring(data).text_content()」
を使っていきます。
画像内で新たにインポートされている「lxml」は、html形式の解析・処理をしてくれます。
「lxml」はこちらで取得出来ると思います。

これで、うまくメールの本文を取得出来ているのではないでしょうか?
それとも、たまたま「123ContactForm」からのメールだったから上手くいってるのかな?
Comments