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

【Yahoo!天気リプレース版】LINE Notify + Pythonで天気情報を取得する方法

はじめに

以前投稿したLINE Notify + Pythonで天気予報を取得する方法で天気予報サイトからスクレイピングした天気予報をLINEで通知する方法を紹介しました。

しかし、ソース元のlivedoor天気予報が2020年7月31日を以ってサービス終了となってしまったため、天気情報の収集先のWebサービスの変更(リプレース)を実施することにしました。

移行先の選定と理由

今回はYahoo!天気を移行先として選びました。
スクリーンショット 2020-07-12 14.07.19.png

選んだ理由は下記。

  • タグ構造がシンプルでlivedoor天気予報のrssとさほど差がなく、修正量を最小限にできる。

  • 天気予報サービスとしてはメジャー。今後もサービス終了する可能性は低い。

Yahoo!天気予報の提供RSSのページを見ると、各地の天気情報のRSSデータを参照することができます。

前回同様、茨城県水戸市のRSSデータを参照したものが下記。
スクリーンショット 2020-07-12 12.12.40.png
titleタグに日付・都市・天気・最高気温/最低気温と欲しい情報がすべて載っています。
descriptionタグが天気情報のサマリーといったところでしょうか。
これらをセットで取得すれば、必要情報はすべて網羅できそうです。
livedoor天気予報のRSSとタグ構造が一緒なのもGOOD.

実行環境

  • OS:Windows10, Mac OS X, Linuxいずれも可。
  • python3
  • beautifulspoup, request
  • raspberry Pi

ソースコード

基本的なロジックは前のエントリと変わりませんが、
天気情報の表示(description)に若干の差異があります。
アイコン出力の判定で使用してるので、記載をYahoo!に合わせています。

例えば、Yahoo!天気では晴一時曇のように●●一時▲▲が追加されているようなので、これをOR条件に加えています。

(例)天気情報に"晴一時曇"または"晴のち曇"または"晴れ時々曇"のワードが出た場合・・・
SunToCloud.png

晴マーク+曇マークが表示されたアイコンが表示される。
 

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

import urllib.request
from bs4 import BeautifulSoup
import requests

icon_path = "アイコンが格納されているパス(絶対パスで入力する)"

# 本番用トークンID
line_notify_token = 'ACCESS TOKEN NAME'
# LINE Notify APIのURL
line_notify_api = 'https://notify-api.line.me/api/notify'

# 抽出対象のRSSとURL(デフォルトは茨城県水戸市)
rssurl = "https://rss-weather.yahoo.co.jp/rss/days/4010.xml"
URL = "https://weather.yahoo.co.jp/weather/jp/8/4010/8201.html"

tenki = []
detail = []

## Parser : 天気情報WebページのHTMLタグから天気情報を抽出してパースするメソッド ##########################
def Parser(rssurl):
   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)

## ck_Weather : 取得した天気情報とそれに応じたアイコンを出力するメソッド ################################
def ck_Weather(i, detail):
   if (detail[i].find("晴")) != -1 and (detail[i].find("曇")) == -1 and (detail[i].find("雨")) == -1 and (detail[i].find("雪")) == -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("晴時々曇")) != -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("晴時々雨")) != -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("晴時々雪")) != -1:
       files = {'imageFile': open(icon_path + "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(icon_path + "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 or (detail[i].find("曇時々晴")) != -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("曇時々雨")) != -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("曇時々雪")) != -1:
       files = {'imageFile': open(icon_path + "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(icon_path + "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 or (detail[i].find("雨時々晴")) != -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("雨時々曇")) != -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("雨時々雪")) != -1:
       files = {'imageFile': open(icon_path + "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(icon_path + "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 or (detail[i].find("雪時々晴")) != -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("雪時々曇")) != -1:
       files = {'imageFile': open(icon_path + "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 or (detail[i].find("雪時々雨")) != -1:
       files = {'imageFile': open(icon_path + "SnowToRain.png","rb")}
       line_notify = requests.post(line_notify_api, data=payload, headers=headers, files=files)

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

   elif (detail[i].find("暴風雪")) == -1:
       files = {'imageFile': open(icon_path + "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)


## メイン処理 ###################################################################################

Parser(rssurl) # 天気予報サイトのHTMLタグから天気情報を抽出
for i in range(0,2):
    message = tenki[i]
    payload = {'message': "\n" + message}
    headers = {'Authorization': 'Bearer ' + line_notify_token}  

    ck_Weather(i, detail) # 天気情報とそれに応じた天気アイコンを出力

message = URL
payload = {'message': message}
headers = {'Authorization': 'Bearer ' + line_notify_token}  # Notify URL
line_notify = requests.post(line_notify_api, data=payload, headers=headers)

################################################################################################

実行結果

Raspberry Piにcron設定して自動実行します。
詳細はLINE Notify + Pythonで天気予報を取得する方法の「実行結果」と「スクリプトを自動実行したい」「天気情報に応じたアイコンを出してみた」の章を参照してください。

image1.png

image0.png

無事にlivedoor天気予報からYahoo!に移植できました。
RSSの中身見る限りだとこちらのほうがシンプルでメンテしやすいかもしれません。

追記

【2020/7/31 記載】
天気が晴時々曇のときに何故か暴風雨のアイコンが出てくるバグを発見。
コードを確認したところ、「曇」の漢字が「雲」になってたので、下記のように修正しました。
(オリジナルコードも修正済です)

GetWeather.py
#(-)
elif (detail[i].find("晴一時曇")) != -1 or (detail[i].find("晴のち曇")) != -1 or (detail[i].find("晴時々雲")) != -1: 
#(+)
elif (detail[i].find("晴一時曇")) != -1 or (detail[i].find("晴のち曇")) != -1 or (detail[i].find("晴時々曇")) != -1: 
S_eki
某メーカーでソフトウェアQAエンジニアやってます。 業務自動化、ライフハックに興味があります。
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