これで私は雨に濡れずに済む(はず)

ここ最近雨が続き、天気予報を見ておらずうっかりびしょ濡れになる機会が増えたため対策を検討することにした。根本的に度を超えた面倒くさがりなので、「天気予報アプリを毎朝開けば良いじゃないか」などというツッコミは受け付けない。基本方針としては、

  1. 京都の天気予報を毎朝6時にツイートする天気予報botを作る
  2. 朝6時の時点で雨が降りそうな時はLine Notifyで通知する

である。特に2に関してだが、毎朝天気予報を通知してくれるサービス(ウェザーニュースの朝刊サービスとかLINE NotifyのIFTTT連携とか)は存在するものの降水が予想される日の朝のみに通知をしてくれるサービスは(無料の範囲内では)存在しないと思われる。私は必要な時だけ通知が欲しいのだ。

さて、以下では上記の方針をpythonスクリプトをheroku上で実行することにより実現していくこととする。

京都の天気予報をツイートするbotを作る

気象情報を取得し、ツイートを生成する

openweathermapの3 hour forcastを利用することにした。api keyを取得したのち、APIを叩いて得られた3時間ごとの天気予報を絵文字に変換し(18時以降は晴れの天気予報であっても月を表示させる)、ツイートする文字列を作成する。

#encoding:utf-8
import urllib3, sys, os
import json

def weatherTranslate(weather, num):
    if "Thunderstorm" in weather:
        return("⚡️")
    elif "Drizzle" in weather:
        return("🌂")
    elif "Rain" in weather:
        return("🌨")
    elif "Snow" in weather:
        return("❄️")
    elif "Atmosphere" in weather:
        return("🌫")
    elif "Clear" in weather:
        if num < 5:
            return("☀️")
        else:
            return("🌙")
    elif "Clouds" in weather:
        return("☁️")

# for openweathermap
http = urllib3.PoolManager()
APPID = "" #api key

# get 3 hours forcast and tweet
resp = http.request('GET', 'http://api.openweathermap.org/data/2.5/forecast?q=Kyoto&mode=json&units=metric&appid=' + APPID).data
resp = json.loads(resp)
Time = [' 6', ' 9', '12', '15', '18', '21', ' 0', ' 3']
print(resp)

tweet = datetime.now().strftime("%m/%d") + 'の京都の天気予報'

for num in range(1, 9):
    weather = weatherTranslate(resp['list'][num]['weather'][0]['main'], num)
    temp = resp['list'][num]['main']['temp']
    tweet = tweet + '\n' +  Time[num - 1] + '時 ' + weather + '  ' + str(temp) + '℃'

生成された文字列をbotに投稿させる

pythonからtwitterに投稿させるために必要なモジュールをインストールする。

pip install python-twitter

bot用のアカウントを作成し、write権限のあるアプリを作成、キーを取得(こちらなどを参照)したのち、以下のスクリプトでツイートを投稿する。

from requests_oauthlib import OAuth1Session

# for twitter
CK = '' # Consumer Key
CS = '' # Consumer Secret
AT = '' # Access Token
AS = '' # Accesss Token Secret
twitter = OAuth1Session(CK, CS, AT, AS)

params = {"status": tweet}
req = twitter.post("https://api.twitter.com/1.1/statuses/update.json", params = params)

朝6時の時点で雨が降りそうな時はLine Notifyで通知する

Line notifyへの登録など

Line上でLine Notifyを友達登録しアクセストークンを発行する必要がある。詳細はこちらを参照。

降水の有無を判断し、通知する

openweathermapのdaily forcastを利用し、悪い天気に該当する場合は傘を忘れないようにとのメッセージを送信する。

#encoding:utf-8
import urllib3, sys, os
import json
import requests

# for line notify
url = "https://notify-api.line.me/api/notify"
token = "" #ラインのアクセストークン
headers = {"Authorization" : "Bearer "+ token}

# for openweathermap
http = urllib3.PoolManager()
APPID = "" #api key

# If it rains today, notify by Line
resptoday = http.request('GET', 'http://api.openweathermap.org/data/2.5/forecast/daily?q=Kyoto&mode=json&units=metric&cnt=1&appid=' + APPID).data
resptoday = json.loads(resptoday)
badWeathers = ["Thunderstorm", "Drizzle", "Rain", "Snow", "Atmosphere"]

weathertoday = resptoday['list'][0]['weather'][0]['main']
print(weathertoday)
for badWeather in badWeathers:
    if badWeather in weathertoday:
        message =  "Today's weather is " + badWeather + ". Take your umbrella with you."
        payload = {"message" :  message}
        r = requests.post(url ,headers = headers ,params=payload)

Heroku上での実行

さて、上記のスクリプトを手動で実行するのでは意味がないので、PaaSとして有名なHeroku上で毎朝定時実行させてみようと思う。

Herokuのアカウント作成

メールアドレスとパスワードを設定すれば簡単にアカウントを作成できる。無料の範囲内で月1000時間までならアプリを起動させていられるので、作成アプリケーションが一つだけなら24時間動かすことが可能である(2017年10月現在)。無料枠を超えた使用やアドオンの追加にはクレジットカードの登録が必要となるようだ。

Herokuを使う準備

ローカルのターミナルからHerokuにログインしてgitを用いてデプロイするのが基本のようである。まずはHerokuのコマンドラインインターフェースをこちらからインストールする。

定時実行のために

Heroku Schedulerというアドオンによりcronタスクを実現できるようだが、先述の通りアドオンの追加にはクレジットカードの登録が必要であるため学生の身では不可能である。そこで、ここではこちらを参考に、APSchedulerを用いて定時実行させることにした。モジュールは次のようにインストールできる。

pip install apscheduler

注意点としては、今回の記事のような方法で使用するとアプリケーションが24時間起動しっぱなしになるということ(逆に、30分経ったらスリープしてしまう、というようなことはない)、cronで定時実行させる場合の時間は太平洋標準時なので日本時間の9時間前を指定しなければならないこと、くらいだろうか。

さて、ツイートを投稿するスクリプトをtweet.py、ラインに通知する方のスクリプトをrainfall.pyとして保存し、上記二つを定時実行するスクリプト(timeclock.py)を書いた。APIの取得はたまに失敗するようなので上手く行かなかった場合はラインに通知するとともに、ツイートの方は一時間空けてリトライさせることにした。

timeclock.py

from apscheduler.schedulers.blocking import BlockingScheduler
import os, time, requests

sched = BlockingScheduler()

# for line notify
url = "https://notify-api.line.me/api/notify"
token = "" #ラインのアクセストークン
headers = {"Authorization" : "Bearer "+ token}

@sched.scheduled_job('cron', hour=21, minute=00)
def timed_job():
    try:
        os.system("python rainfall.py")
    except:
        message =  "Failed to get rainfall information."
        payload = {"message" :  message}
        r = requests.post(url ,headers = headers ,params=payload)

    time.sleep(60)

    try:
        os.system("python tweet.py")
    except:
        message =  "Failed to get today's weather. Retry after 1 hour."
        payload = {"message" :  message}
        r = requests.post(url ,headers = headers ,params=payload)
        time.sleep(3600)
        try:
            os.system("python tweet.py")
        except:
            message =  "Retry Failed."
            payload = {"message" :  message}
            r = requests.post(url ,headers = headers ,params=payload)

sched.start()

デプロイに必要なファイルの準備

まずHerokuにデプロイさせるディレクトリを作成し、上記の3つのスクリプトを入れる。実際にアプリケーションを動作させるためには、下記のファイル群が必要である。

Procfile

どのスクリプトをどのように動かすか、ということを指定するファイル。拡張子なし。

clock: python timeclock.py
runtime.txt

使用するpythonのバージョン(ローカルではpython -Vで確認できる)を記載する。

python-3.6.0
requirements.txt

アプリケーションで使用するモジュールとそのバージョンを記載したファイル。pip freeze > hoge.txtにより現在インストールされているモジュールのバージョンが記載されたファイルを作成できるが、これをそのまま使うとデプロイ時にnot foundが多発して発狂する(私がanaconda環境であるのがいけなかったのだろう)。必要なものだけを選ぶと以下のようになった。

oauth2client==4.1.0
oauthlib==2.0.6
python-twitter==3.3
requests==2.18.4
requests-oauthlib==0.8.0
jsonschema==2.5.1
urllib3==1.22
APScheduler==3.4.0
future==0.16.0

デプロイする

ターミナルで先ほど作成したディレクトリに入り、

heroku login

を実行する。メールアドレスとパスワードを入力してログインできたら、

heroku create
git init
git add .
git commit -m "My first commit"
git push heroku master

によりデプロイが完了する。起動させるためには

heroku ps:scale clock=1

を実行させれば良い。

その他のコマンド

アプリケーションの停止

heroku ps:scale clock=0

スクリプトを手動で動かしてみる

heroku run python tweet.py

ログの取得

heroku logs

2回目以降のデプロイ

git add .
git commit -m "適当なコメント"
git push heroku master

感想など

作成したtwitterbot@kyoto__weatherである。APIの取得に失敗することがある他、openweathermapの天気予報の精度が異常に悪い(気象庁の予報と大きく食い違っていることがある)ので今後の検討課題。

ちなみに、天気の急変に備えるためには日本気象株式会社がリリースしているiOSアプリ「雨降りアラート」が秀逸である。雨雲の接近を通知してくれるため降雨の数時間前に天候の悪化を知ることができる。

参考にしたページ

簡単!Herokuで動くTwitter botをPythonで実装する - Qiita

http://www.icrus.org/horiba/article/2014_11_22_01.php

Pythonで作ったTwitterのbotをHerokuで動かす - ひとメモ