プロセスの終了時にGmailを送信する【Python】

今回は，少し前にPythonでGmailを使う方法を学んだので，それをまとめておきたいと思います．
それから，コマンドを実行し，その結果をGmailで送信するシェルスクリプトも作りましたので，その紹介も．

参考：
<blockquote>ベースとなったもの：<a href="http://unoh.github.io/2007/06/13/python_2.html" target="_blank" title="unoh.github.com by unoh:">unoh.github.com by unoh:</a>
やりたいことそのもの：<a href="http://www.sekailab.com/wp/2013/12/11/watch-process-to-send-gmail/" target="_blank" title="Pythonでプロセスを監視して終了したらgmailを使ってメール送信する | せかいらぼ:">Pythonでプロセスを監視して終了したらgmailを使ってメール送信する | せかいらぼ:</a>
画像の送信について：<a href="http://blog.livedoor.jp/vine_user/archives/51575120.html" target="_blank" title="独学Linux : Python スクリプトでGmail経由のメールを送信する方法:">独学Linux : Python スクリプトでGmail経由のメールを送信する方法:</a>
SMTPに関して：<a href="http://blog.livedoor.jp/shf0811/archives/17013716.html" target="_blank" title="プログラミングと慶應通信 : Pythonでgmailの送受信:">プログラミングと慶應通信 : Pythonでgmailの送受信:</a>
日本語の扱いについて：<a href="http://qiita.com/yasunori/items/265d8db746742bb967c4" target="_blank" title="Pythonで日本語メールを送る方法をいろいろ試した - Qiita:">Pythonで日本語メールを送る方法をいろいろ試した - Qiita:</a>
</blockquote>


#Pythonを使ってGmailを送信する

まず，参考にしたコードをコピペしてみて，動くかどうか試しましたが，一応動きました．あとはこれを外から呼び出したりするときに使いやすいようにカスタマイズするだけです．また，日本語のエンコードの問題や，セキュリティ面を考えて，さらにいくつかのサイトを参考にして，改良しました．

いいとこ取りしただけですが，とりあえず載せておきます．

```py:sendGmail.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate
from platform import python_version
import argparse
import getpass
import codecs

release = python_version()
if release > '2.6.2':
    from smtplib import SMTP_SSL
else:
    SMTP_SSL = None

def create_message(from_addr, sender_name, to_addr, subject, body, encoding):
    msg = MIMEText(body, 'plain', encoding)
    msg['Subject'] = Header(subject, encoding)
    form_jp = u"%s <%s>" % (str(Header(sender_name, encoding)), from_addr)
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Date'] = formatdate()
    return msg

def send_via_gmail(from_addr, to_addr, passwd, msg):
    if SMTP_SSL:
        print "send via SSL..."
        s = SMTP_SSL('smtp.gmail.com', 465)
        s.login(from_addr, passwd)
        s.sendmail(from_addr, [to_addr], msg.as_string())
        s.close()
        print 'mail sent!'
    else:
        print "send via TLS..."
        s = smtplib.SMTP('smtp.gmail.com', 587)
        if release < '2.6':
            s.ehlo()
        s.starttls()
        if release < '2.5':
            s.ehlo()
        s.login(from_addr, passwd)
        s.sendmail(from_addr, [to_addr], msg.as_string())
        s.close()
        print "mail sent!"

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Send Gmail.')
    parser.add_argument('-t', '--to', dest='to_addr', type=str,
                        default='デフォルトの送信先',
                        help='To address')
    parser.add_argument('-s', '--subject', dest='subject', type=str,
                        default='デフォルトのタイトル', help='Title')
    parser.add_argument('-b', '--body', dest='body', type=str,
                        default='no message', help='Body of the mail.')
    args = parser.parse_args()

    from_addr = "送信元のGmailアドレス"
    sender_name=u'送信元の名前'
    print "from: %s <%s>" % (sender_name, from_addr)
    passwd = getpass.getpass()
    to_addr = args.to_addr
    title = args.subject
    body = args.body
    msg = create_message(from_addr, sender_name, to_addr, title, body, 'utf-8')
    send_via_gmail(from_addr, to_addr, passwd, msg)
```

前半のバージョンチェックは，他の環境でも動くためにはこれくらい必要なのかもしれません（コピペですみません）．argparseを使えば，オプションの扱いが楽になるようなので，今回はそれを導入しました．

ちなみに，argparseを使えばヘルプを表示させることも簡単で，

![Selection_008.png](https://qiita-image-store.s3.amazonaws.com/0/54869/876854d0-eb5d-f113-4216-46250c2530a1.png)

のように表示してくれます．

それ以外の基本の部分は，参考にしたサイトのものとほとんど同じです．SSLの方がTLSよりセキュリティ的には良いようです．また，Googleアカウントのパスワードを直で書くのには抵抗があると思うので，直接実行する場合には，キーボードからの入力を受け付け，getpassを使うことで打った内容が表示されないようにしてあります．


#プロセスの終了を待って，メールでお知らせ

なぜわざわざPythonからメールを送りたいかというと，時間のかかる計算を研究室のパソコンでさせておいて，自分は家に帰って結果が出てきたらそれを受け取る，ということをしたかったからです．なんで？って，なんかかっこいいじゃないですか！

・・・ということで，今度はその部分をシェルスクリプトで書くことにします．基本的にはPythonスクリプトを対象にしているのですが，汎用性を考えて，コマンド自体を引数に与える形にしました．

コードはこちら．

```bash:psWatchMail.sh
if [ $# = 0 ]; then
    echo "need argument 'COMMAND'" 1>&2
    exit 0
else
    COMMAND=$1
fi
subject="command \"${COMMAND}\" done"

result=`${COMMAND}`

wait

sleep 1

python /スクリプトまでのパス/sendGmail.py -t "送信先メールアドレス" -s "${subject}" -b "${result}"

echo "${result}"

exit 0
```

これにより，

```bash
sh psWatchMail.sh "実行するコマンド"
```

とコマンドを指定することにより，そのコマンドが終了した時点で，スクリプト内で指定した宛先に，

「command "実行するコマンド" done」

という題名で，その標準出力を本文としたメールが送ることができます．最初はPIDなどを指定してプロセスが生きているかどうかの判定を行う方法を考えて，その方面で調べていたのですが，こっちの方がシンプルでわかりやすいので，こちらの方法を使うことにしました．どちらの場合にしろ，欠点はプロセスが完全に終了しないとメールが送れないということで，今まで作ったようなGUIのシミュレーションとは相性が悪いです．まぁ，初めから時間のかかるとわかっている部分があったら，逆にそのPythonスクリプトの内部でsendGmail.pyを呼び出すようにしておけば，任意のタイミングでメールを送ることができるので，それはそれでいいでしょう．もしくは，CPUの使用率をモニターして，ある閾値を下回ったら終了したと判定してもいいかもですね．これはおいおい考えることにします．

実行例は，

![Selection_009.png](https://qiita-image-store.s3.amazonaws.com/0/54869/e22dbed7-8c84-033e-62f6-cd9413cea1c9.png)


のようになります．Gmail側からも

![Selection_010.png](https://qiita-image-store.s3.amazonaws.com/0/54869/c18c7ce6-0197-7762-c196-d38ab8f2ed4b.png)

と，メールが送信されていることが確認できます．


#まとめ

PythonでGmailを使う方法をまとめました．しかし，これはGmailに限らず，他のメールクライアントからも送ることができます．その場合，メールサーバのアドレスとポート番号を変更してやればいいです．僕の場合，実際に大学のメールアカウントのバージョンも作っていて，ターミナルからメールを打つこともあります．便利かと言われると微妙ですが．でも，いちいちログイン認証とか，重たいサイトを開かないといけなくてイラッとする人には向いてると思います．（これじゃ僕が心の小さい人みたいじゃないか！）ともかく，これで家と研究室，両方のパソコンで放置プレイが可能になりました．重たい計算ももう怖くないですね！