Help us understand the problem. What is going on with this article?

LINE Notify + Pythonで天気情報を取得する方法

はじめに

本記事は今年の4月に投稿したものです。
ちょうどAdvent Calendarにpythonのコーナーがあったため、
説明内容を追記して再更新しました。

前回の記事はこちら→Pythonでブラックジャック作った。

きっかけ

天気情報をLINEに送る仕組みがあれば便利だなと思っていたら
LINE Notifyというサービスがあることを知り、早速調べてみました。

LINE Notifyでアクセストークンの取得する。

まずは、LINE Notifyにアクセスし、自分のLINEアカウントにログインした上で通知を行いたいチャットに対してアクセストークンの発行を行う必要があるとのこと。早速やってみました。

アクセストークンの取得手順は下記エントリが非常に分かりやすかったのでここでは割愛。

■PythonでLINEに通知を送る
https://qiita.com/hiro-abe/items/e42f857bd6b40bc178a3

取得する天気情報データ

天気情報は、livedoorの天気予報のRSSから取得。
http://weather.livedoor.com/

例えば、水戸の天気予報を取得する手順を記す。

1.水戸市の天気を開き、RSSのボタンを押下する。
  http://weather.livedoor.com/area/forecast/080010

スクリーンショット 2018-01-14 23.07.49.png

2.下記のようなXMLファイルが開かれる。今回取得する天気情報はずばりコレです。

スクリーンショット 2018-01-14 23.12.44.png

通知機能の実装

天気情報を取得し、LINEに通知する仕組みを実装する。

上記の天気情報は、Pythonのライブラリの1つである
BeautifulSoupを用いて取得可能である。

未だインストールしていない方は、pipで所得。

$ pip install beautifulsoup4

ソースコードはこちら。
変数"line_notify_token"には、LINE Notifyで取得したアクセストークンを入力して下さい。

GetWeather.py
#!/usr/bin/env python

import urllib.request
from bs4 import BeautifulSoup

import requests
line_notify_token = 'ACCCESS TOKEN NAME'
line_notify_api = 'https://notify-api.line.me/api/notify'

# Get Weather Information (Default:Mito, Ibaraki, Japan)
rssurl = "http://weather.livedoor.com/forecast/rss/area/080010.xml"

tenki = []
with urllib.request.urlopen(rssurl) as res:
    xml = res.read()
    soup = BeautifulSoup(xml, "html.parser")
    for item in soup.find_all("item"):
        title = item.find("title").string
        if title.find("[ PR ]") == -1:
            tenki.append(title)

for i in range(0,2):
    message = tenki[i]
    payload = {'message': message}
    headers = {'Authorization': 'Bearer ' + line_notify_token}  # Token
    line_notify = requests.post(line_notify_api, data=payload, headers=headers)




実行結果

ターミナル上で実行したところ、

image1.jpeg

当日と翌日の水戸の天気と最高気温を簡単に取得できちゃいました。これは便利!

スクリプトを自動実行させたい

ちょうど家用のファイルサーバ導入目的で購入したRaspberry Pi3があったので、こちらでcronを回して運用することにしました。

ちなみに運用イメージはこんな感じです。
名称未設定.png

ターミナル開いて、crontab -eを実行して下記を入れるだけ。
OSがLinuxだととても簡単です。

#分 時 日 月 曜日 <実行コマンド>
00 06 * * * python3 <pythonファイル格納先の絶対パス>
00 08 * * * python3 <pythonファイル格納先の絶対パス>

上記の例では毎日午前6時、午後6時に指定したpythonスクリプトを実行します。
実行ファイルの指定は絶対パスで指定してください。
(絶対パスで指定しないとうまく動かなかった・・・)

天気情報に応じたアイコンを出してみた

通知内容がテキストだけだと天気情報が直感的にわかりにくいので
下記のように、テキストとアイコンをセットで出すようにしてみました。
・「晴れ」だったら晴れのアイコンを出す
・「晴れのち雨」だったら晴れと雨が順に並んでいるアイコンを出す

↑こんな感じ。直感的に使いやすくなった気がします。

ソースはは下記、だいぶ記載量増えてますが
やってることはメッセージに出てくる天気情報を判別してアイコンを出すようにしているだけ。

「◯◯のち▲▲」と「◯◯時々▲▲」は分けるとパターンが複雑になるため、一緒のアイコンを出すようにしています。

GetWeather.py
#!/usr/bin/env python

import urllib.request
from bs4 import BeautifulSoup
import requests

line_notify_token = 'ACCCESS TOKEN NAME'
line_notify_api = 'https://notify-api.line.me/api/notify'

# Get Weather Information (Default:Mito, Ibaraki, Japan)
rssurl = "http://weather.livedoor.com/forecast/rss/area/080010.xml"

tenki = []
detail = []

with urllib.request.urlopen(rssurl) as res:
    xml = res.read()
    soup = BeautifulSoup(xml, "html.parser")
    for item in soup.find_all("item"):
        title = item.find("title").string
        description = item.find("description").string

        if title.find("[ PR ]") == -1:
            tenki.append(title)
            detail.append(description)


for i in range(0,2):
    message = detail[i]
    payload = {'message': message}
    headers = {'Authorization': 'Bearer ' + line_notify_token}  # Token

    if (detail[i].find("晴")) != -1 and (detail[i].find("曇")) == -1 and (detail[i].find("雨")) == -1 and (detail[i].find("雪")) == -1:
       files = {'imageFile': open("/home/pi/GetWeather/Sun.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("晴時々曇")) != -1 or (detail[i].find("晴のち曇")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/SunToCloud.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("晴時々雨")) != -1 or (detail[i].find("晴のち雨")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/SunToRain.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("晴時々雪")) != -1 or (detail[i].find("晴のち雪")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/SunToSnow.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("曇")) != -1 and (detail[i].find("晴")) == -1 and (detail[i].find("雨")) == -1 and (detail[i].find("雪")) == -1:
       files = {'imageFile': open("/home/pi/GetWeather/Cloud.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("曇時々晴")) != -1 or (detail[i].find("曇のち晴")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/CloudToSun.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("曇時々雨")) != -1 or (detail[i].find("曇のち雨")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/CloudToRain.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("曇時々雪")) != -1 or (detail[i].find("曇のち雪")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/CloudToSnow.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("雨")) != -1 and (detail[i].find("晴")) == -1 and (detail[i].find("曇")) == -1 and (detail[i].find("雪")) == -1:
       files = {'imageFile': open("/home/pi/GetWeather/Rain.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("雨時々晴")) != -1 or (detail[i].find("雨のち晴")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/RainToSun.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("雨時々曇")) != -1 or (detail[i].find("雨のち曇")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/RainToCloud.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("雨時々雪")) != -1 or (detail[i].find("雨のち雪")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/RainToSnow.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("雪")) != -1 and (detail[i].find("晴")) == -1 and (detail[i].find("雨")) == -1 and (detail[i].find("曇")) == -1:
       files = {'imageFile': open("/home/pi/GetWeather/Snow.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("雪時々晴")) != -1 or (detail[i].find("雪のち晴")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/SnowToSun.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("雪時々曇")) != -1 or (detail[i].find("雪のち曇")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/SnowToCloud.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("雪時々雨")) != -1 or (detail[i].find("雪のち雨")) != -1:
       files = {'imageFile': open("/home/pi/GetWeather/SnowToRain.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("暴風雨")) == -1:
       files = {'imageFile': open("/home/pi/GetWeather/Typhon.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    elif (detail[i].find("暴風雪")) == -1:
       files = {'imageFile': open("/home/pi/GetWeather/HeavySnow.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

    else:
       line_notify = requests.post(line_notify_api, data=payload, headers=headers)

なお、アイコンはフリー素材のサイトのものを使用しております。
PNGでダウンロードし、指定したディレクリパスに格納しておけばOKです。
スクリーンショット 2019-12-05 22.51.02.png

少し強引に書きすぎたため、時間見つけてリファクタリングしてみます。
もしもっとスマートな書き方をご存じの方いたら教えていただけると嬉しいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away