yahoo!天気のrssデータでpythonの天気予報データ読上げプログラムをリプレース
Raspberrypi を使用して電光掲示板で、時刻、天気予報及びニュースを定時に表示していることは、過去の記事(Pythonでコマンドを呼出し天気予報を電光掲示板に表示、ラズパイ電光掲示板/天気予報の表示内容を変えてみる)で何度かお知らせしています。
この度、電光掲示板の天気予報表示に違和感を感じました。8月半ばで「危険な暑さ」と騒がれているのに、最高気温が 31℃、最低気温が 23℃と表示されているんです。
その原因は、電光掲示板に流れる日付を見て氷解しました。
8/15になっているのに、天気予報は7月31日の天気を出していたのです。
以前も、ラズパイが瞬断した際、同じような現象が起きたことがありましたが、今回は事情が違い、ニュースは正しい日付で表示されているのです。
原因はサービス停止
猛暑の中ですが、原因究明に入りました。
まずは、天気予報を流している shell を動かしてみましょう。
start
140010
Traceback (most recent call last):
File "/home/pi/python-pg/tenki_file.py", line 116, in
main()
File "/home/pi/python-pg/tenki_file.py", line 13, in main
txt_weather()
File "/home/pi/python-pg/tenki_file.py", line 50, in txt_weather
obj = json.loads( r.read().decode('utf-8') )
File "/usr/lib/python3.5/json/init.py", line 319, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.5/json/decoder.py", line 339, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.5/json/decoder.py", line 357, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 4)
convert end
add border end
size change end
案の定、エラーが発生していますね。本来 value が来るべき箇所に、value でないものが来ているようなことを言っています。
天気予報の内容を受領する次の命令でエラーが発生しているようです。
obj = json.loads( r.read().decode('utf-8') )
以前、天気予報の内容が、うまく受領できないときに追加した命令です。
ウムム、ひょっとして、ここで使用している json.loads の引数が変わっとかしてエラーが発生したのかと勝手に解釈し、ネットの世界をググってみました。
さんざん格闘しましたが原因がつかめません。
そこで、json データを受領しているURLをブラウザ上から検索してみました。
livedoor のページが検索されます。
数時間気づかなかったのですが、ふと「何で、jsonコードが表示されないのか?」という疑問に思い当たりました。
そこで、別のラズパイで天気予報を読み上げる同じ jsonコードを使ったプログラムを実行してみたんですが、何とこちらもニュースを喋ってくれません。
再実行したログを見たところ、電光掲示板側で出たものと全く同じエラーが表示されます。
勘の鈍い60爺ですが、ここでやっと気がつきました。ひょっとして天気予報の配信サービスが終了したのではないかと・・・。
早々ニュースを当たってみたところ livedoor の天気予報のサービスが7月31日をもって終了していたことがわかりました。
これが原因だったのですが、気付くのに半日以上を費やしてしまいました。
対策
原因がわかったことで、その対策に入ります。対策と言っても、動かなくなった以下の pythonプログラムを修正するだけのことです。
- 天気予報を読み上げている pythonプログラム
- 電光掲示板で天気予報を表示している pythonプログラム
簡単に言えば、現在、livedoor から受信する json形式のデータ取出し部分を、他社からのデータ受信に変更すればいいだけのことです。
ところが、別会社で無料(!)で json形式データを提供しているところを探したのですが、日本だと見つからないんです。
OpenWeatherMap などが代替候補として挙がりましたが、当然、帰ってくる内容は英語表記です^^;
熟慮しましたが、やはり、英語表記のデータを扱うことは止めておこうという結論に達しました(天気の内容を天気マークに置き換える部分がかなり大変に見えました。参考資料①)。
そこで、json形式でデータを取出す部分を rss形式で取り出すように変更することにしました。
そして、rss形式でデータを配信している中で、大手でサービスを簡単には止めないであろう「Yahoo!天気」からデータを受信することに決めました。
天気予報読上げpythonプログラムリプレース
上記2つの修正プログラムのうち、まずは、「天気予報を読み上げている pythonプログラム」のリプレースから手を付けました。
その理由ですが、電光掲示板では天気予報マークを設定する必要があり、上述したとおり、天気の内容から天気マークを取り出す部分が手間取りそうだと判断したからです。
訂正箇所
① データ取出し部分
rss形式でデータを取り出す部分ですが、参考資料②の内容を丸々コピーすることで、天気予報データを手に入れることが出来ました。
【訂正前】
json_url = 'https://weather.livedoor.com/forecast/webservice/json/v1' #API URL
try:
r = urllib.request.urlopen('%s?city=%s' % (json_url, city) )
obj = json.loads( r.read().decode('utf-8') )
【訂正後】
## 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)
### Execute
# 抽出対象のRSS(神奈川県横浜市)
rssurl = "https://rss-weather.yahoo.co.jp/rss/days/4610.xml"
② 今日の最高気温、最低気温を追加
今までのプログラムは、最高気温と最低気温は明日のものしかなかったんですが、今日の最高気温と最低気温を読み上げるように訂正します。
【訂正前】
# TODAY
cast = forecasts[0]
today_w_txt = weather_text % (cast['dateLabel'], cast['telop'])
# TOMMOROW
cast = forecasts[1]
temperature = cast['temperature']
tommorow_w_txt = weather_text % (cast['dateLabel'], cast['telop'])
tommorow_t_txt = temperature_text % (cast['dateLabel'], temperature['max']['celsius'], temperature['min']['celsius'])
# SAY
weather_str = title + ' ' + today_w_txt + ' ' + tommorow_w_txt + ' ' + tommorow_t_txt
【訂正後】
for i in range(0,2):
detail2 = detail[i].split()
if i == 0:
kyo_tenki = detail2[0]
temp = detail2[2].split('/')
kyo_max = temp[0]
kyo_min = temp[1]
else:
asu_tenki = detail2[0]
temp = detail2[2].split('/')
asu_max = temp[0]
asu_min = temp[1]
title = u'神奈川県横浜の天気'
# TODAY
today_w_txt = weather_text % ('今日', kyo_tenki)
today_t_txt = temperature_text % ('今日', kyo_max, kyo_min)
# TOMMOROW
tommorow_w_txt = weather_text % ('明日', asu_tenki)
tommorow_t_txt = temperature_text % ('明日', asu_max, asu_min)
# SAY
weather_str = title + ' ' + today_w_txt + today_t_txt + ' ' + tommorow_w_txt + ' ' + tommorow_t_txt
その他の訂正
① BeautifulSoup インストール
この天気情報を取得するのに、Python のライブラリのひとつである BeautifulSoup を用いていたため、下記のインストールが必要でした。
60爺のは、python3 なので以下のコマンドで実施しました。
sudo pip3 install beautifulsoup4
ソースコード
rssコードの取得部分が参考資料のコピーをすることで簡単に実現出来たので、思いのほか時間をかけずに pythonプログラムの訂正が出来ました。
title については、livedoor のようになっておらず、以下のような総合データが出てしまうので、リテラルで対応しました。
【 18日(火) 東部(横浜) 】 晴のち曇 - 34℃/27℃ - Yahoo!天気・災害
その結果、以前と遜色ないレベルで天気予報の読み上げを実現できました。
jsay 8月20日、10時26分7秒
/usr/local/bin/jsay 8月20日、10時26分7秒
jsay '神奈川県横浜の天気 今日の天気は晴れです。今日の予想最高気温、35℃度、予想 最低気温、26℃度です。 明日の天気は晴れです。 明日の予想最高気温、35℃度、予想最低気温、26℃度です。'
最後に、(余り美しくないプログラムですが)リプレースしたソースコードを掲載しておきます。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import shlex
import subprocess
from datetime import datetime
import urllib.request
from bs4 import BeautifulSoup
CMD_SAY = 'jsay'
def main():
say_datetime()
say_weather()
return
def say_datetime():
d = datetime.now()
text = '%s月%s日、%s時%s分%s秒' % (d.month, d.day, d.hour, d.minute, d.second)
text = CMD_SAY + ' ' + text
print (text)
text = '/usr/local/bin/' + text
print (text)
proc = subprocess.Popen(shlex.split(text))
proc.communicate()
return
def say_weather():
weather_text = u'%sの天気は%sです。'
temperature_text = u'%sの予想最高気温、%s度、予想最低気温、%s度です。'
Parser(rssurl) # 天気予報サイトのHTMLタグから天気情報を抽出
for i in range(0,2):
detail2 = detail[i].split()
if i == 0:
kyo_tenki = detail2[0]
temp = detail2[2].split('/')
kyo_max = temp[0]
kyo_min = temp[1]
else:
asu_tenki = detail2[0]
temp = detail2[2].split('/')
asu_max = temp[0]
asu_min = temp[1]
title = u'神奈川県横浜の天気'
# TODAY
today_w_txt = weather_text % ('今日', kyo_tenki)
today_t_txt = temperature_text % ('今日', kyo_max, kyo_min)
# TOMMOROW
tommorow_w_txt = weather_text % ('明日', asu_tenki)
tommorow_t_txt = temperature_text % ('明日', asu_max, asu_min)
# SAY
weather_str = title + ' ' + today_w_txt + today_t_txt + ' ' + tommorow_w_txt + ' ' + tommorow_t_txt
#weather_str = weather_str.encode('utf-8')
text = '''%s '%s' ''' % (CMD_SAY, weather_str)
print (text)
proc = subprocess.Popen(shlex.split(text))
proc.communicate()
return
## 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)
### Execute
# 抽出対象のRSS(神奈川県横浜市)
rssurl = "https://rss-weather.yahoo.co.jp/rss/days/4610.xml"
tenki = []
detail = []
detail2 = []
temp = []
if __name__ == "__main__":
main()
この後、「電光掲示板で天気予報を表示している pythonプログラム」の対応に入りたいと思います。
最後に
電光掲示板に表示される天気予報の違和感に気づいたのが2週間ほどたってからです。
危険な暑さと騒いでいるのにもかかわらず、電光掲示板には31℃の表示なのでやっと気づいたんですよ!
そして原因究明を行ったところ、livedoorの天気予報配信が終了していたとはびっくりと言うよりがっかりでした。
json形式での天気配信がないことで、rss形式のyahoo!天気のrssデータでpythonの天気予報データ読上げプログラムをリプレースせざるを得ないことになってしまいました。
大変だった!
■思えば、「openjtalk」の記事も増えてきました
※参考
①天気予報APIを使って天気を表示してみた
②【Yahoo!天気リプレース版】LINE Notify + Pythonで天気情報を取得する方法
ディスカッション
コメント一覧
まだ、コメントがありません