ラズパイ:Yahoo!災害情報から指定した地域の警報や注意報を取得してテキスト化する

Raspberry Pi

昨今は、毎日が異常気象である。
Yahoo!天気・災害のサイトからラズパイで指定した地域の警報や注意報などを取得してテキスト化するPythonプログラムを作った。

実行イメージ

例として茨城県の「水戸市」と「日立市」の情報のみ取り出している。

先頭は取得した地域のタイトル。
以下特別警報・警報・注意報の順に改行して表示される。(データを取得したときは特別警報はなかった)

茨城県の警報・注意報 - Yahoo!天気・災害

水戸市
        大雨注意報      洪水注意報
日立市
        大雨警報
        波浪注意報      高潮注意報

ちなみに警報や注意報が出ていない場合はこんな感じ。

茨城県の警報・注意報 - Yahoo!天気・災害

水戸市
        警報・注意報はありません
日立市
        大雨注意報      波浪注意報

今回の実行環境

Raspberry Pi2
OS: Raspbian 9.13 stretch
Python 3.5.3

ソースコード

スクレイピングにbeautifulsoup4を利用しているためpipコマンドでインストールしておく。

pip3 install bs4

checkSaigai.py

#!/usr/bin python3
# -*- coding: utf-8 -*-

"""
	ラズパイ(Raspberry Pi)でYahoo!のサイトから災害情報を取得する
		2021-08-15
		
		python 3.5.3
		OS: Raspbian 9.13 stretch(Raspberry Pi2)にて確認
"""
# スクレイピング関連
import urllib.request
from bs4 import BeautifulSoup

# Yahoo!の災害トップ > 警報・注意報のサイト > 地域のURLを開く
url = "https://typhoon.yahoo.co.jp/weather/jp/warn/8/"		# 例)茨城県
data = urllib.request.urlopen(url)

# HTMLを解析して取得
soup = BeautifulSoup(data, 'html.parser')

# 表示用文字列
texts = ""

# 地域タイトル取得
title = soup.find("title").text
texts += "{}\n\n".format(title)

# classが 'warnArea_table' のタグを取得(表全体のデータとなる)
saigaiTable = soup.find(class_="warnArea_table")

# テーブルの1行ずつを順に処理する
for tr in saigaiTable.find_all("tr"):
	# 市町村名を取得
	city = tr.find("a").text		# aタグに市町村名が入っている
	
	# 指定した市町村名の情報のみ取得する → 【 and city != "(市町村名)"】 で取得したい市町村名を追加
	if city != "水戸市" and city != "日立市":
		continue
	else:
		texts += "{}\n".format(city);	# 市町村名を文字列に追加

	# 特別警報、警報、注意報を取得(全てリストとして取得される)
	emgWarnings = tr.find_all(class_="icoEmgWarning")	# 特別警報
	warnings = tr.find_all(class_="icoWarning")				# 警報
	advisorys = tr.find_all(class_="icoAdvisory")			# 注意報

	# 特別警報・警報・注意報が全て出ていない場合
	if len(emgWarnings) == 0 and len(warnings) == 0 and len(advisorys) == 0:
		texts += "\t警報・注意報はありません\n"

	# どれか一つでも出ていた場合
	else:
		# 特別警報・警報・注意報ごとに文字列保存
		if len(emgWarnings) != 0:		# 特別警報
			texts += "\t"
			for emgWarning in emgWarnings:
				texts += "{}\t".format(emgWarning.text)
			texts += "\n"
	
		if len(warnings) != 0:			# 警報
			texts += "\t"
			for warning in warnings:
				texts += "{}\t".format(warning.text)
			texts += "\n"
	
		if len(advisorys) != 0:			# 注意報
			texts += "\t"
			for advisory in advisorys:
				texts += "{}\t".format(advisory.text)
			texts += "\n"

# 取得した災害情報を表示
print(texts)

ソースコード解説

テキスト化した値は全て1つの変数に保存

実行イメージで表示される情報は、全て1つの変数textsに追加していくものとする。

texts = ""

都道府県ごとのURLをチェック

まずは取得したい地域のサイトURLが必要になるため、Yahoo!警報・注意報のサイトにアクセス。

警報・注意報 - Yahoo!天気・災害
防災に役立つ最新の特別警報、警報、注意報、今後の推移を市区町村レベルで提供。

例として茨城県をクリック。

各都道府県ごとに警報や注意報の情報が表示されている
今回はこのサイトから必要な情報を抜き出すことを考える。
このとき表示されるURLがプログラムに必要になるため、コピーしておく。

コピーしたURLを16行目の変数urlに代入する。

url = "https://typhoon.yahoo.co.jp/weather/jp/warn/8/" # 例)茨城県

先ほどのサイトで表示されていた市町村の中から取得したい市町村名を38行目のif文に設定する。
複数ある場合はandでつなげる。比較式は != であることに注意。

if city != "水戸市" and city != "日立市":

設定は以上。

スクレイピング部分

Yahoo!天気・災害の都道府県ごとのページは、市町村ごとのデータをtableタグで表示している。
スクレイピングに必要なタグ情報としては以下の通りだった。

地域名titleタグ
テーブル全体class=”warnArea_table”
市町村名trタグ内のaタグ
特別警報class=”icoEmgWarning”
警報class=”icoWarning”
注意報class=”icoAdvisory”

地域名は単純にtitleタグの情報を取り出すのみ。(beautifulsoup4ではタグも含めて取り出されるため、.textをつけて文字列のみを変数に代入している)

title = soup.find("title").text

市町村名、特別警報、警報、注意報のデータは全て class=”warnArea_table” のtableタグに入っている。

表全体は、以下で変数saigaiTableに取り出している。

saigaiTable = soup.find(class_="warnArea_table")

表の行(trタグ)ごとに各市町村のデータが入っていることが分かったので、trタグをキーとしてループでまわして行ごとに処理をする。(trタグは複数あるためfind_allメソッドで取り出している)
以下の saigaiTable.find_all(“tr“) には、リスト型で返ってくるのでリストの1件ずつ変数trに取り出している。

for tr in saigaiTable.find_all("tr"):
	:

trタグ内にはaタグが1つしか無く、aタグの文字列が市町村名であった。以下で取り出す。

city = tr.find("a").text	

取り出した市町村名から必要な地域のみを取り出すようにする。
水戸市日立市だけを取り出したい場合は以下のようにする。(水戸市と日立市以外はループの先頭に戻って情報取得の処理をしないようにしている)

if city != "水戸市" and city != "日立市":
	continue
else:
	texts += "{}\n".format(city);	# 市町村名を文字列に追加

特別警報警報注意報の処理はタグのクラス名が違うだけで基本的に処理は同じ。
ただ、警報や注意報が一つとは限らないためfind_allで取得している。

emgWarnings = tr.find_all(class_="icoEmgWarning")	# 特別警報
warnings = tr.find_all(class_="icoWarning")		# 警報
advisorys = tr.find_all(class_="icoAdvisory")		# 注意報

上記処理の戻り値が警報や注意報によっては無い場合もある。その時はリストの要素数が特別警報・警報・注意報の3つとも0となるため、len関数を使って以下のように「警報がありません」との情報をテキストに追加するようにしている。

if len(emgWarnings) == 0 and len(warnings) == 0 and len(advisorys) == 0:
	texts += "\t警報・注意報はありません\n"

特別警報警報注意報がどれか1つでも出ていた場合は、タグは以下のようなイメージになっている。

例)注意報の場合

<span class="icoAdvisory"><span>大雨<small>注意報</small></span></span>
<span class="icoAdvisory"><span>波浪<small>注意報</small></span></span>

上記だと class=”icoAdvisory” は2か所あるので、リストの要素数は2になっている。
リストに格納された情報を取り出してテキストに追加していく。

例)注意報の場合

if len(advisorys) != 0:			# 注意報
	texts += "\t"
	for advisory in advisorys:
		texts += "{}\t".format(advisory.text)
	texts += "\n"

最後に変数textsの内容を表示して終了。

print(texts)

コメント

タイトルとURLをコピーしました