気象庁のサイトから XML を取得して、天気予報を出力する Python のスクリプトについてまとめておきます。
以下の3つの環境で動作確認をしています。
環境1: Windows パソコン
Python 3.X、Anaconda、Beautiful Soup をインストール済み。
環境2: Raspberry Pi (bullseye)
環境3: VPS (CentOS 系 Linux)
背景 ~ 天気予報を取得してみる
公共機関などでの公開データは XML (RSS) 形式となっていることが多いです。
そこで XML を Python で扱う一例として、気象庁のサイトから天気予報のデータを取得して出力するスクリプトをまとめておくことにします。
スクリプトの URL などを修正することで使いまわしが可能です。
なお、各サーバーへのアクセスや取得したデータの著作権・取り扱いについては、各サイトの規定に従う必要があります。サーバーに負荷をかけるような使用は禁止されていることが多いため、配慮いただきますようお願いします。
設定&使い方
① 以下を参考に、パソコンにフォルダを作ってください。Windows の場合の例を示します。Linux の場合も、同様に動かすことが可能です。
例: C:\user\weather1
② ①のフォルダの中に weather_forecast1.py 等の名前でテキストファイルを作成し、下記のスクリプトをコピー&ペーストして保存してください。
③ プロンプト(または、Anaconda Prompt)を起動し、②のスクリプトを実行してください。
例: “python C:\user\weather\weather_forecast1.py”
→ 実行すると、ネットから天気予報のデータを取得して、①のフォルダ内に、weather_forecast1.txt という名前でデータを保存します。
サンプルスクリプトの説明
スクリプトの動き
スクリプトでは、気象庁のサイトの作りに応じて、xml ファイルの取得を2回行ない、天気予報のデータを取得するようにしています。
スクリプトを実行すると、まず、以下の URL (気象庁のサイト)にアクセスします。
リンク先: https://www.data.jma.go.jp/developer/xml/feed/regular_l.xml ★1
このファイルに記載されているリンクの中で、”VPFW50″ という文字列を含むリンクが1週間の天気予報を含むファイル名に対応しています。
また、”130000″ という文字列が、東京エリアに対応しています。東京以外の天気予報を取得する場合は、上記のリンクを確認し、対応するエリアの数値に差し替えてください。
つぎに、”VPFW50_130000″ という文字列(東京の1週間の天気予報に対応)を含むリンクをすべて引き抜きます。東京の1週間の天気予報に対応しています。天気予報は時間の経過に従って刻々と出されるため、複数のファイルが抽出されます。
抽出されるリンクは複数あり、最後に抽出されるリンクが最新日時での天気予報のファイル名に対応しています。そこで、スクリプトでは、引き抜いた複数のリンクのうち、最後に見つかったリンク(★2)を抽出します。
つぎに、取得したリンク(★2)にアクセスし、xml を取得します。
具体的には、以下のようなファイル名になっています。
https://www.data.jma.go.jp/developer/xml/data/20240430014252_0_VPFW50_130000.xml
1週間の天気予報を記載した xml ファイルは、年月日、時刻を含んだファイル名になっています。
つまり、最新のファイル名は、固定ではなく、つねに変わっていくことになります。加えて、過去のファイルは、順次、サーバーから消去されるようになっています。したがって、★2は、時間が経つとアクセスできなくなります。
このため、スクリプトでは、★1からファイル名を抽出し、最新の天気予報(★2)を取得しています。
この後、この xml (★2)を読み込み、ファイルの構造から、天気予報に対応する部分の文字列を引き抜いています。結果を、テキストデータで書き出します。
データの引き抜き方は、まず、上記のリンクを開き、ピラミッド構造になっている xml の各項目で、欲しい情報が上からたどっていくつ目の階層、いくつ目の項目番号に対応しているか確認します。
つぎに、その番号をたどるよう、配列の添え字を設定すると、対応する文字列を引き抜くことが可能です。xml の詳細については、記載を省略します。時間が取れれば、後日、記載を追加しようと思います。
出力の例
書き出したテキストデータ weather_forecast1.txt の例はつぎのようになっています。
2024/05/01,214,--,くもり後雨
2024/05/02,201,40,くもり時々晴れ
2024/05/03,101,60,晴れ時々くもり
2024/05/04,101,40,晴れ時々くもり
2024/05/05,101,20,晴れ時々くもり
2024/05/06,200,20,くもり
2024/05/07,202,20,くもり一時雨
上記は、2024年の4月30日にスクリプトを実行した場合の出力の例です。
予報ですので、翌日の5月1日から、1週間の7日分の天気予報を取得できます。
日付、天気の種別、雨が降る確率、天気を取得することが可能です。
まとめ
天気予報を例に、XML データを取得する方法についてスクリプトの形でまとめました。
ネット検索をしても、実用で使える程度にまとまったサイトが見つからないようでしたので、ポイントを整理して公開しておくことにします。
他にも、指定したサイトマップの XML からリンクを取得するスクリプトなどについてもまとめています。興味のある方は、関連リンクも参照してみてください。
関連リンク
・ 指定したサイトマップからリンクを抽出する 【Python】
・ 指定した URL からリンクを抽出する 【Python】
・ CSV ファイルを読み込んで機械学習と予測をする 【scikit-learn】
外部リンク
・ 気象庁 | 著作権・リンク・個人情報保護について (jma.go.jp)
・ 気象庁 | 気象庁防災情報XMLフォーマット (kishou.go.jp)
・ 気象庁 | 気象庁情報カタログ (jma.go.jp)
・ 府県予報区等 – コロプレス(塗り分け)地図 | 気象庁防災情報発表区域データセット
・ 府県予報区等 発表区域データ | 気象庁防災情報発表区域データセット (nii.ac.jp)
・ 気象庁|過去の気象データ・ダウンロード (jma.go.jp)
・ https://docs.python.org/ja/3/library/xml.etree.elementtree.html
・ https://docs.python.org/3/library/index.html
サンプルスクリプト weather_forecast1.py
import os
import urllib.request as rq1
from bs4 import BeautifulSoup as bs1
import xml.etree.ElementTree as et1
def write1( file1, str1 ):
with open( file1, 'w', encoding='utf-8' ) as f1:
f1.write( str1 )
return 0
def get_urls0( url0 ):
str1 = rq1.urlopen( url0 )
soup1 = bs1( str1, features='xml' )
urls1 = soup1.find_all( 'link' )
a1 = []
for url1 in urls1:
url2 = url1.get( "href" )
if ".xml" in url2:
a1.append( url2 )
a1.sort()
return a1
def get_url1( url0, str1 ):
a1 = get_urls0( url0 )
a2 = []
for i1 in range( len(a1) ):
if str1 in a1[i1]:
a2.append( a1[i1] )
return a2
def get_xml1( url1 ):
rq2 = rq1.Request( url1 )
with rq1.urlopen( rq2 ) as res1:
res2 = res1.read()
xml1 = et1.fromstring( res2 )
return xml1
def get_weather_forecast0( xml1 ):
str1 = ""
for i1 in range( 7 ):
str2 = str( xml1[2][0][0][0][i1][0].text ) # date and time
str3 = str( xml1[2][0][0][1][0][0][1][i1].text ) # weather forecast
str4 = str( xml1[2][0][0][1][0][0][2][i1].text ) # types
str5 = str( xml1[2][0][0][3][1][0][1][i1].text ) # probability
str2 = str2.split( "T" )[0].replace("-", "/")
str5 = str5.replace( "None", "--" )
str6 = str2 + "," + str4 + "," + str5 + "," + str3 + "\n"
str1 = str1 + str6
return str1
def get_weather_forecast1():
url0 = "https://www.data.jma.go.jp/developer/xml/feed/regular_l.xml"
print( url0 )
str1 = "VPFW50_130000"
print( str1 )
a1 = get_url1( url0, str1 )
# print( a1 )
url1 = a1[ len(a1)-1 ]
print( url1 )
xml1 = get_xml1( url1 )
print( xml1 )
str1 = get_weather_forecast0( xml1 )
print( str1 )
path1 = os.path.dirname(__file__) + "/"
file1 = path1 + "weather_forecast1.txt"
write1( file1, str1 )
return
if __name__ == "__main__":
get_weather_forecast1()