Hatena::ブログ(Diary)

風柳メモ このページをアンテナに追加 RSSフィード Twitter

2011-05-18

Google App Engine/Python等で日本語のメールを扱いやすくするモジュール

GAE/Pythonで、素直にこのドキュメントの記述に従ってメール受信処理を書こうとすると、特に日本語等の取り扱いで文字化けに悩まされてしまうようです。

そこで、もうちょっとだけましに取り扱えるようなモジュールを書いてみました。

すっかり忘れていたGoogle App Engineのメールの扱いづらさに辟易中 - 風柳メモの続きだったり。
モジュール自体はMailHookを作った当時に書いたソースほぼそのまま。2009年11月だから、あの後GAEの方も改善されてたりするのかな〜。

そういやhookhubも無くなってMailHookもほとんど使えないんだよな〜

ダウンロード

email_decoderモジュール

ZIPファイルを解凍すると出てくるemail_decoder.pyを用います。Python 2.5、2.6で動作確認。

使い方の例

# email_decoderクラスのインポート
from email_decoder import email_decoder

# MIMEエンコードされたメールを読み込んでデコード処理を行い、mailオブジェクトを作成
mail = email_decoder(mime_encoded_mail_text)

# タイトル(Subject)表示
print mail.subject

# 本文(text/plain)表示
print mail.get_body_plain()

# 本文(text/html)表示
print mail.get_body_html()

# To 表示(1)
print u'To: %s' % (mail.to)

# To 表示(2)
to_list = mail.listaddr('to',address_only=False)
print u'To: %s' % (u', '.join([u'%s <%s>' % (_n if _n else _a,_a) for (_n, _a) in to_list]))

その他の使い方はソースのif __name__ == '__main__':以降にもいくつか例があるので参照して下さい(手抜き)

Google App Engine/Pythonで使用する場合

GAE/Pythonでメールを受信する場合、ドキュメントに従って、InboundMailHandlerを継承したclassに

import logging
from google.appengine.ext.webapp.mail_handlers import InboundMailHandler
 
class MyMailHandler(InboundMailHandler):
  def receive(self, mail):
    logging.info(u'Subject: ' + mail.subject)

のように、receive()を定義しますが、この代わりに、

import logging
from google.appengine.ext.webapp.mail_handlers import InboundMailHandler
from email_decoder import email_decoder
 
class MyMailHandler(InboundMailHandler):
  def post(self):
    mail = email_decoder(self.request.body)    
    logging.info(u'Subject: ' + mail.subject)

のようにして、post()を定義し、MIMEエンコードされたメールの本体が含まれるself.request.bodyをemail_decoder()の引数に指定して使用します。

それにしても…

Free quota per app per dayPrice
Email API100 recipients$0.01 / 100 recipients

Google cloud services ? App Engine

Freeだと今まで2000通/日受信出来たのが、100通/日に減らされるのか……。

さくらのレンタルサーバでメール受信時にスクリプトを起動させる設定メモ

先程の記事で書いたように、GAEでFreeの受信メール数が制限されて厳しくなるため、個人的に使用しているさくらのレンタルサーバを使って

  • さくら側でメールを受信
  • メールを解析して、内容をGAE側にHTTP POSTで送信

のような処理をすることを見越し、とりあえず、さくらのレンタルサーバでメールを受信したときにスクリプトを起動させる方法を調べてみた。

■参考

情報科雑感: GmailとSAKURAでOpenPNEの携帯メール投稿設定

メール - 「さくらのレンタルサーバー」と「さくらのメールボックス」に関する情報 - mailfilterについて

スクリプト処理処理専用のメールアドレスを作成

幸い、さくらのレンタルサーバではメールアドレスをいくつも持てるので、

  1. 専用のメールアドレスを作成
  2. 当該アドレスの設定で[追加]ボタンを押し、適当なメールアドレス(ダミー)を指定した後、「○転送専用にする」にチェックを入れる

のようにしておく。

別に2.については実行しなくてもよいかも。

.mailfilterの設定変更

上記のようにして新規にメールアドレスを作成すると、

/home/アカウント名/MailBox/作成したメールアドレス名/

のようなディレクトリの下に、.mailfilterというファイルが出来ている。

この内容は(転送専用メールの場合)、

cc "!ダミーで指定したメールアドレス"

exit

のようになっているが、これを

to "| スクリプト実行ファイルのフルパス スクリプトのフルパス"

exit

のように変更する。


例えば、Pythonスクリプトの場合は、

to "| /usr/local/bin/python /home/account/mailhook/mailhook.py"

exit

のような感じに。

なお、.mailfilterはパーミッションが600である必要有り。telnet(ssh)経由で直接変更する場合はいいが、ftp(sftp)等で転送する場合はパーミッションの確認を忘れないこと。

後は実際に実行するスクリプト(上記例では~/mailhook/mailhook.py)を適当に作成して配置しておく。

メール処理にはemail_decoderもそれなりに使えるかも知れない。

さて、実際にどういうサービスを作れるか?

例えば「タイプ:リアルタイム」・「頻度:その都度」のGoogle アラートを作成しておき、上記のようなメールアドレスに転送するようにしておけば(Googleのアカウントで管理する場合はアラートの送信先がGMail固定になるみたいなので、GMail上でフィルタを作って転送をかけるようにしておけばよい)、(cron等で定期ポーリングすることなく)メール受信をトリガにして興味のある情報収拾と加工が自動的にできるようになる、かな。

実例と挫折の経緯

手ごろな題材として、

ほんのつぶやき

というbotを動かす処理を改修して試してみた。


このbotは、cronで定期的にTwitterAPIを使って「@yonda4 」を検索し、その結果を使って、@yonda4宛に発言したユーザにmentionを送る、というもの。

ところが、Twitter Search APIはアクセス制限がきついため(特にGoogle App Engineからの場合・同一IPアドレスからの検索と見なされてしまうためらしい?)、代わりにGoogle アラートで検索キーワードを「site:twitter.com "@yonda4"」にしたアラートを作成して、これをトリガにつぶやく、という処理に変更してみた。


結果としては一応、動くには動いたのだが、Google アラートだと結構とりこぼしが大きいため(それとある時点ではリアルタイム検索に上がってこなかったものがその後でインデックスに追加された場合、追加された分についてはアラートが送られてこない模様)、これについては挫折。現在はcron処理に戻している。

といってもそのままでは無く、Twitter Searchは止めて、直接twitterで読書記録。読んだ4!APIを使うように変更してみた。


とりこぼしがあることを前提に、おおまかな検索結果でもリアルタイムで得られれば効果が高い、というサービスの場合はいいかもしれない。

また、mention(@screen_name)反応型のbotであれば、cron無しでも作れそうではある(とりこぼしは発生するけれど、botなら「きまぐれなんです」といいはれる(笑))。

GAE/Goの開発環境をFedora 12上に構築したので覚書

Google App Engineで、PythonJavaに続いてGoのランタイム環境が提供された、とのこと。

よく解らないながら、とりあえず開発環境だけでも整えておくかぁ、と思ったところ、今のところSDKはLinuxまたはMac OSでしか提供されていない模様。

とりあえず、手元にあるFedora 12上で、SDKが動作するところまで構築してみたので、覚書。

最初はFedora Core 5という古い環境で試そうとしたが、最終的にGoのコンパイルがうまく行かないみたいなので挫折したのは秘密…。

■参考

Virtual Linux envirionments using real partitions for dual boot system

Python/インストール/Linux/Python - MinacoWiki

方針

  • Go開発専用のアカウント(gaego)を用意する。
  • Fedora 12だとPython 2.6系が入っているが、GAEのSDKが正規にサポートしているPython 2.5をインストールする。
  • GAEのSDKで必要なSSLとPILは入れておく。
  • Windowsからリモート接続して動作確認したいため、X Server(Xming)を動かしておく。
  • Xming上でも日本語は入力出来るようにしておく。

手順

Go開発専用アカウント(gaego)の作成
# adduser -m gaego
# passwd gaego
# su - gaego
$ mkdir -p ~/temp

$HOME直下にtempディレクトリを作成してそこで作業する。

Go SDKのダウンロードと展開
$ cd ~/temp
$ wget http://googleappengine.googlecode.com/files/go_appengine_sdk_linux_386-1.5.0.zip
$ unzip go_appengine_sdk_linux_386-1.5.0.zip -d ~/

$HOME/google_appengine/というフォルダが作成され、そこのファイルが格納される。

Python 2.5.5のインストール
$ cd ~/temp
$ wget http://www.python.org/ftp/python/2.5.5/Python-2.5.5.tgz
$ tar xvfz ./Python-2.5.5.tgz
$ cd Python-2.5.5
$ ./configure --prefix=$HOME
$ make
$ make install

configureに--prefix=$HOMEを指定することで、$HOME下にインストールされる。$HOME下にはbin、include、lib、shareというフォルダが作成され、その下に各ファイルが格納される。

Go SDKとPython 2.5.5にPATHを通す
$ vi ~/.bash_profile

PATH=$HOME/google_appengine:$HOME/bin:$PATH

のように、$HOME/google_appengineと$HOME/binが先頭に来るようにPATH環境変数を変更する。

$ source ~/.bash_profile
$ python -V
Python 2.5.5

のように、Python 2.5.5が動作することを確認する。

SSLモジュールのインストール
$ cd ~/temp
$ wget http://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz
$ tar xvfz ssl-1.15.tar.gz
$ cd ssl-1.15
$ make
$ make install

easy_installのインストール
$ cd ~/temp
$ wget http://peak.telecommunity.com/dist/ez_setup.py
$ python ez_setup.py

PILのインストール
$ easy_install -UZ PIL

X Client側(Linux)に、接続時にgnome-terminalを起動するスクリプトを準備
$ vi ~/xming_terminal.sh

#! /bin/bash
ibus-daemon -d -x
source ~/.bash_profile
export XMODIFIERS=@im=ibus
gnome-terminal --geometry=132x43

ibus-daemonの起動でiBusを用いた日本語入力を可能にしているつもり。標準だと[半角/全角]キーで切替。

$ chmod +x ~/xming_terminal.sh

X Server(Windows側・Xming)を準備

Download - Xming X Server for Windows - SourceForge.JPから、Xming-6-9-0-31-setup.exeXming-fonts-7-5-0-34-setup.exeをダウンロードし、各々Windowsにインストールする。

その後「XLaunch」を起動し、上記で作成したスクリプト(xmiing_terminal.sh)を起動するようなランチャを作成する。

Select display settings
  ◎ Multiple windows
  Display number: 0

Select how to start Xming
  ◎ Start a program

Enter or choose one X client to Run Local or Run Remote
  Start program: $HOME/xming_terminal.sh
  Run Rmote
    ◎ Using PuTTY(plink.exe)
      Connect to computer: (LinuxのIPアドレスまたはHOST名)
      Login as user: gaego
      Password (leave blank if using PuTTy pageant): 

Passwordは何も書かないようにする(ランチャ起動時にプロンプトが起動して確認される)。もしここに書いてしまうと、平文で保存されてしまうので要注意。

Specify parameter settings
    レ Clipboard
    Additional parameters for Xming:
    -dpi 96

-dpi 96はターミナルのフォントサイズを大きくする設定。標準(75dpi)が小さいと思ったら指定しておく。

Configuration complete
  [Save configuration]

[Save configuration]で任意の名前で設定ファイル(ランチャ)を保存しておく。次回からはこれをダブルクリックすることで、保存した設定でXmingが起動される。

ランチャ起動後、gnome-terminalを閉じたとしても、X Server(Xming)は自動では終了してくれないため、タスクトレイ上に残ったXmingを手動で→クリック→「Exit」して終了すること。

"Hello, world!"アプリの試行

Hello, World! - Google App Engine — Google Developersを参考に、app.yaml及びhello.goファイルを用意する。

今回の試行では、開発用のディレクトリ($HOME/go)を用意し、その下にmyappを置いて、

/home/gaego/go/myapp
 ├─app.yaml
 └─hello
     └─hello.go

のような感じにして試した。

後は、Windows上から、上記で作成したランチャによりgnome-terminalを起動し、

$ firefox &
$ cd ~/go
$ dev_appserver.py --port=8080 myapp

のようにして、Firefox(バックグラウンドで起動)およびdev_appserverを起動した後、Firefoxから

http://localhost:8080/

にアクセスしてやると、問題が無ければ、

Hello, world!

が表示される。

独り言

これが、入れるだけで満足してしまって(もしくは疲れてしまって)、後が続かない症候群かぁ……。