天気予報を取得してテキストで出力する 【Python】

Python

サイトから XML (RSS) を取得して出力するスクリプトについてまとめておきます。
具体的には、Python の標準パッケージを使って、天気予報のデータ XML を読み込みます。出力は、テキストファイルで出力する事例について記載します。

以下の環境で動作確認をしています。
環境: Windows パソコン、Python 3.X、Anaconda、Beautiful Soup をインストールしています。

背景

機械学習の検討をしていると、サイトから過去の時系列のデータを取得して、学習用データとして使用したくなることがあります。
例えば、日付つきのログデータを自力で取得していたとき、関係がありそうな過去の天気や気温のデータをサイトから取得し、学習用データを作成したくなることがあります。これらのデータを使って機械学習を実行し、当日の天気、将来の天気予報のデータから、当日や将来の予測をしたいといった場合です。
わかりやすい事例では、年月日、曜日、休日と天候から、店舗の売り上げを予測するといったケースです。
また、雨が降ると渋滞するなどもよく経験すると思います。天気予報から、渋滞を予測することもかなりの確度で可能と考えられます。

こうした時系列のデータは、現状では、公共機関で RSS で公開されていることが多いです。
RSS 形式とはいっても、実質的には XML 形式のデータをダウンロードして、各タグの文字列さえ取得できれば、あとは何とかなるといった状況です。

そこで、まずは、最も典型的な事例として、天気予報のデータを取得して出力するスクリプトをまとめておくことにします。
具体的には、気象庁の天気予報のサイトを例に、XML で公開されているデータを取得するサンプルについてまとめておきます。データの取得だけできればよいのであれば、出力部分を修正することで使いまわしが可能です。

なお、各サーバーへのアクセス、取得するデータの取り扱いについては、各サイトの規定に従う必要があります。取得したデータの公開や、サーバーに負荷をかけるような使用は禁止されていることが多いため、各サイトでの確認をお願いいたします。

使い方

① 以下を参考に、パソコンにフォルダを作ってください。Windows の場合の例を示します。
例: C:\user\weather1
② ①のフォルダの中に weather_forecast1.py 等の名前でテキストファイルを作成し、下記のスクリプトをコピー&ペーストして保存してください。
③ プロンプト(または、Anaconda Prompt)を起動し、②のスクリプトを実行してください。
例: “python C:\user\weather\weather_forecast1.py”

→ 実行すると、ネットから天気予報のデータを取得して、プロンプトに表示後、weather_forecast1.txt という名前でデータを保存します。

サンプルスクリプトの説明

スクリプトの動き

スクリプトを実行すると、指定した以下の URL (気象庁のサイト)にアクセスします。
リンク先: https://www.data.jma.go.jp/developer/xml/feed/regular_l.xml ★1
このファイルに記載されているリンクの中で、”VPFW50″ という文字列を含むリンクが1週間の天気予報を含むファイル名に対応しています。
また、”130000″ という文字列が、東京エリアに対応しています。東京以外の天気予報を取得する場合は、上記のリンクを確認し、対応するエリアの数値に差し替えてください。

つぎに、”VPFW50_130000″ という文字列を含むリンクをすべて引き抜きます。抽出されるリンクは複数あって、最後のファイルが最新日時での天気予報のファイル名に対応します。そこで、引き抜いた最後のリンク(最新のファイル名・リンク)を抽出します。

つぎに、取得したリンクにアクセスし、xml を取得します。以下が一例です。
例: https://www.data.jma.go.jp/developer/xml/data/20220505074225_0_VPFW50_130000.xml
1週間の天気予報を記載した xml ファイルは、年月日、時刻を含んだファイル名になっています。つまり、最新のファイル名は、固定ではなく、つねに変わっていくことになります。このため、スクリプトでは、★1からファイル名を抽出しています。
この後、この xml を読み込み、ファイルの構造から、天気予報に対応する部分の文字列を引き抜いています。結果を、テキストデータで書き出します。

データの引き抜き方は、まず、上記のリンクを開き、ピラミッド構造になっている xml の各項目で、欲しい情報が上からたどっていくつ目の階層、いくつ目の項目番号に対応しているか確認します。
つぎに、その番号をたどるように配列の添え字を設定すると、対応する文字列を引き抜くことが可能です。xml の詳細については、後日、記載を追加しようと思います。

★ XML のリンク先の修正について  2022年5月
これまで、Yahoo! の天気予報の xml を参照するサンプルコードを載せていました。ところが、2022年3月末でサービスが終了となりました。
そこでサンプルコードを、気象庁のサイトの xml を参照するよう差し替えました。そのため、解説の詳細はスキップし、動くサンプルコードと関連リンク等を公開しておくことにします。

まとめ

天気予報を例に、XML データを取得する方法についてスクリプトの形でまとめました。
ネット検索をしても、実用で使える程度にまとまったサイトが見つからないようです。
そこで、ポイントを整理して公開しておくことにします。

これで、XML や RSS の取得なども自由自在です。無数にあるサイトのデータを有効活用できることになります。
他にも、指定したサイトマップの XML からリンクを取得するスクリプトなどについてもまとめています。興味のある方は、関連リンクを参照してみてください。

関連リンク
・ バッチファイルで Anaconda から Python を実行する方法 【Windows 10】
・ 指定したサイトマップからリンクを取得する 【Python】
・ CSV ファイルを読み込んで機械学習と予測をする 【scikit-learn】

外部リンク
・ 気象庁 | 著作権・リンク・個人情報保護について (jma.go.jp)
・ 気象庁 | RSS配信について (jma.go.jp)
・ 気象庁 | 気象庁情報カタログ (jma.go.jp)
・ 府県予報区等 発表区域データ | 気象庁防災情報発表区域データセット (nii.ac.jp)
・ https://docs.python.org/ja/3/library/xml.etree.elementtree.html
・ https://docs.python.org/3/library/index.html

サンプルスクリプト


import urllib.request as rq1 
import xml.etree.ElementTree as et1 
import os 
from bs4 import BeautifulSoup

# https://docs.python.org/ja/3/library/xml.etree.elementtree.html
# https://docs.python.org/3/library/index.html

def write1( file1, str1 ): 
    with open( file1, 'w', encoding='utf-8' ) as f1: 
        f1.write( str1 ) 
    return 0 

def get_urls0( url0 ): 
    print( url0 ) 
    res1  = rq1.urlopen( url0 ) 
    soup1 = BeautifulSoup( res1, 'html.parser' )
    urls1 = soup1.find_all( 'link' ) 
    a1 = [] 
    for url1 in urls1: 
        url2 = url1.get( "href" ) 
        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 show_xml0( xml0 ): 
    print( "----------" ) 
    print( type( xml0 ) )                # type 
    print( xml0 )                        # xml 
    print( xml0.tag )                    # tag 
    print( xml0.text )                   # text 
    print( xml0.attrib )                 # attribute 

def show_xmls1( xml1 ): 
    show_xml0( xml1[1][0] )                          # Tokyo, Week 
    show_xml0( xml1[2][0][0][1][0][0][0] )           # weather 
    show_xml0( xml1[2][0] )                          # metrological inforamtion 

    show_xml0( xml1[2][0][0][0][0][0] )              # date and time 
    show_xml0( xml1[2][0][0][0][1][0] )              # 
    show_xml0( xml1[2][0][0][0][2][0] )              # 
    show_xml0( xml1[2][0][0][0][3][0] )              # 
    show_xml0( xml1[2][0][0][0][4][0] )              # 
    show_xml0( xml1[2][0][0][0][5][0] )              # 
    show_xml0( xml1[2][0][0][0][6][0] )              # 

    show_xml0( xml1[2][0][0][1][0][0][0] )           # weather forecast 
    show_xml0( xml1[2][0][0][1][0][0][1][0] )        # 
    show_xml0( xml1[2][0][0][1][0][0][1][1] )        # 
    show_xml0( xml1[2][0][0][1][0][0][1][2] )        # 
    show_xml0( xml1[2][0][0][1][0][0][1][3] )        # 
    show_xml0( xml1[2][0][0][1][0][0][1][4] )        # 
    show_xml0( xml1[2][0][0][1][0][0][1][5] )        # 
    show_xml0( xml1[2][0][0][1][0][0][1][6] )        # 

    show_xml0( xml1[2][0][0][1][0][0][2][0] )        # types 
    show_xml0( xml1[2][0][0][1][0][0][2][1] )        # 
    show_xml0( xml1[2][0][0][1][0][0][2][2] )        # 
    show_xml0( xml1[2][0][0][1][0][0][2][3] )        # 
    show_xml0( xml1[2][0][0][1][0][0][2][4] )        # 
    show_xml0( xml1[2][0][0][1][0][0][2][5] )        # 
    show_xml0( xml1[2][0][0][1][0][0][2][6] )        # 

    show_xml0( xml1[2][0][0][3][1][0][0] )           # probability 
    show_xml0( xml1[2][0][0][3][1][0][1][0] )        # 
    show_xml0( xml1[2][0][0][3][1][0][1][1] )        # 
    show_xml0( xml1[2][0][0][3][1][0][1][2] )        # 
    show_xml0( xml1[2][0][0][3][1][0][1][3] )        # 
    show_xml0( xml1[2][0][0][3][1][0][1][4] )        # 
    show_xml0( xml1[2][0][0][3][1][0][1][5] )        # 
    show_xml0( xml1[2][0][0][3][1][0][1][6] )        # 

def get_weather_forecast1( 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 
        str6 = str2 + "," + str3 + "," + str4 + "," + str5 + "\n" 
        str1 = str1 + str6 
    return str1 

url0 = "https://www.data.jma.go.jp/developer/xml/feed/regular_l.xml" 

str1 = "VPFW50_130000" 
a1 = get_url1( url0, str1 ) 

url1 = a1[ len( a1 )-1 ] 
print( url1 )  

xml1 = get_xml1( url1 )                  # xml element tree 

# show_xmls1( xml1 )                     # only for the analysis 

str1 = get_weather_forecast1( xml1 )     # text 
print( str1 ) 

path1 = os.path.dirname(__file__) + "/" 
file1 = path1 + "weather_forecast1.txt" 
write1( file1, str1 ) 



 

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