天気予報を取得してHTMLで出力する 【Python】

Python

サイトから XML (RSS) を取得して HTML で出力するスクリプトについてまとめておきます。
具体的には、Python の標準パッケージを使って、天気予報のデータ XML を読み込みます。最小限の知識、手間で済むよう意図しています。

以下の環境で動作確認をしています。
環境: Windows 10、Python 3.X、Anaconda

背景

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

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

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

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

使い方

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

→ 実行すると、ネットから天気予報のデータを取得して、プロンプトに表示後、weather_forecast1.txt という名前でデータを保存します。
加えて、HTML ファイル(weather_forecast1.html)を保存します。ダブルクリックしてブラウザで確認してみてください。取得した URL でリンクを張っており、リンクで飛ぶようにしています。

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

関数の説明

・ 最初に urllib.request、xml.etree.ElementTree、os をインポートします。
新たなライブラリをインストールするのは煩雑ですし、バージョンの組み合わせにより誤動作の原因となります。
そこで、できる限り標準ライブラリのみを使うようにしています。
・ つぎに、def でいくつか関数を定義しています。
・ write1() は、テキストを書き出すための関数です。取得した天気予報のデータをテキストファイルで出力する際、また、HTML ファイルを出力する際に使います。
・ get_xml1() は、サイトから xml ファイルをダウンロードする関数です。
・ show_xml0() は、ダウンロードした xml の要素の詳細を表示するためのデバッグ用の関数です。
・ show_xml1() も同じく、xml の要素を表示する関数です。xml を調べる際に使います。
・ generat_html1() は、html ファイルを生成するための関数です。
・ get_weather_forecast1() は、xml のデータから、天気予報のデータに該当する要素を取得する関数です。
各サイトの xml データを参照して作成しています。
サイトごとに取得したい要素の場所が変わるため、サイトを変えた場合はこの関数も修正する必要があります。
・ get_weather_forecast2() は、html ファイルを生成するための関数です。具体的には、天気予報の文字列部分を a タグで囲んで、HTML の文字列を作っています。この文字列を HTML のひな型 generate_html1() に差し込んで、html ファイルを作っています。

スクリプトの動き

スクリプトを実行すると、指定した URL (Yahoo! 天気の東京の天気予報データ)から、xml のデータを取得します。
その後、天気予報部分の文字列データを取得して、テキストデータを書き出しています。
続いて、HTML 用のデータを生成して、HTML を書き出しています。

XML ファイルの説明

・ XML の一例を確認します。サンプルスクリプトで使用しているものです。
例: https://rss-weather.yahoo.co.jp/rss/days/4410.xml
・ 上記のリンクを確認すると、以下がわかります。
・ <rss …> タグの下に、<channel> タグ(index 0)があります。
・ そして、<channel> タグの下に、<title>タグ(index 0)、<link>タグ(index 1)、<descriptin>タグ(index 2)、<copyright>タグ(index 3)、<language>タグ(index 4)、<lastBuiltDate>タグ(index 5)、<item>タグ(index 6)、<item> タグ(index 7)、…、<item>タグ(index 13)、<item>タグ(index 14)、… のタグがあります。
・ この <channel> タグの直下には、13個以上のタグが並んでいますが、取得したい天気予報のデータは、<item> タグとなっている部分です。上記のインデックス index でいえば、index 6, 7, 8, 9, 10, 11, 12, 13 です。これらのタグ・インデックスは、当日の天気予報と、その後の1週間の天気予報に対応しているため、8個の要素となっています。index 14 以降の <item> は天気予報とはなっていないため、不要です。
・ さらに、この <item> タグの下には、<title>タグ(index 0)、<link>タグ(index 1)、<description> タグ(index 2)、<pubDate> タグ(index 3)の4つのタグが入っています。
天気予報の文字列のデータは、<title> タグ(index 0)に入っています。
また、各日付ごとの詳細データの URL が <link> タグ(index 1) に入っています。
・ 上記で説明したように、各サイトで公開されている XML ファイルは、ツリー構造となっています。トップの要素の下に、各要素が順次、ぶら下がる形式で記載されます。
・ xml の確認をするため、xml のタグ情報の表示をするのが show_xmls1() 関数です。スクリプトではコメントアウトさせていますが、# を外して実行してみてください。# を外すと、タグの情報をコンソールに各要素の内容を表示できます。show_xmls1() 関数で、取得したい場所のタグのデータを引き抜くことができることが確認できれば、あとはスクリプトを書いていくだけです。
・ 天気予報に対応するタグを取得しているのが、get_weather_forecast1() 関数です。また、HTML 用の文字列を生成しているのが、get_weather_forecast2() 関数です。
天気予報の情報は、xml のツリー構造で、index 0 → index 6 ~ 13 → index 0 (★1)をたどると、タグに囲まれたテキストデータとして取得できることになります。
また、URL の情報は、xml のツリー構造で、index 0 → index 6 ~ 13 → index 1 (★2)とたどると、テキストデータで取得できることがわかります。
そこで、各関数で、str2 = xml1[0][6+i1][0].text とすることで、天気予報の部分のテキストを取得しています。[] 内の数値が、順に、★1に対応しています。
また、str3 = xml1[0][6+i1][1].text とすることで、URL を取得しています。[] 内の数値が、順に、★2に対応しています。

うまくうごいたら

取得するデータの差し替え、出力データの修正

うまく動いたら、サイト(url1 = “https://….xml” としたところ)の差し替えをしてみてください。
例えば、Yahoo! の RSS については、以下のリンクで公開されています。
・ https://weather.yahoo.co.jp/weather/rss/

サンプルスクリプトでは東京の天気としていますが、各地の天気に差し替える程度であれば、サンプルコード内で url1 としている部分を差し替える程度で動くと思います。

サンプルスクリプトでは、出力するデータはテキストファイルと HTML ファイルとしています。いずれか一方しか必要がないようでしたら、コメントアウトするなどして処理を外してください。
また、出力するファイルのファイル名に年月日を入れれば、過去のデータを蓄積していくことが可能となります。天気予報については、過去の天気はサイトからダウンロードできるようですので不要ですが、過去のデータについてサイトから RSS を取得できない場合は、日付つきのファイル名で保存しておくと便利です。

データ収集の自動実行

・ また、タスクスケジューラから1日1回程度、上記の Python スクリプトを実行するようにすると、当日以降の天気予報などを自動で取得し、HTML ファイル形式で自動で出力できるようになります。
例えば、バッチファイルから上記の Python スクリプトを自動実行するようにし、そのバッチファイルをタスクスケジューラに設定して自動実行するようにします。Python スクリプトを複数実行したい場合は、バッチファイルに追記するようにします。
・ または、Raspberry Pi などの Linux をお持ちであれば、Linux に移植して自動化するのもおすすめです。
HTML ファイルの生成先を共有フォルダとして公開しておけば、他のパソコンから HTML を参照できるようになります。
・ なお、HTML のデザインは現状では全く考慮していません。実用化する場合は、デザインを改善してみてください。

さらに応用

サンプルスクリプトは天気予報の事例です。天気以外であっても、各サイトで RSS 形式、または XML 形式で公開されているデータがあれば、自動で取得できることになります。

ネットニュースでよく見るサイトについて、もし RSS が公開されていれば、上記の URL を差し替えることで、データの自動収集が可能となることになります。
例えば、主要な複数のニュースサイトのデータを一括で自動収集して集約するなど、応用が可能です。
時系列データは、将来/過去の天気、ニュース以外にも、温度、湿度、日照データ、日経平均株価、為替などがあります。すでに公開されているサイトが多数あります。

なお、XML を差し替えたとき、XML の構成ごとに抽出したいデータの場所が変わりますので、関数を修正する必要があります。具体的には、get_weather_forecast1()、get_weather_forecatt2() を参考に、あらたに関数を定義するか、XML のツリー構造にしたがって、取得箇所を書き換えれば動くと思います。
出力ファイルも、現在は、テキストファイルと HTML ファイルとしています。RSS/XML ファイルが取得できるサイトと、出力する HTML のファイル名を何種類か決めて自動実行するようにすれば、情報収集の自動化・効率化が実現できると考えられます。
こうした基礎データが集まれば、例えば、CSV ファイルから機械学習を実行するスクリプトについてもまとめていますので、組み合わせでの応用範囲が格段に広がることにもなります。

まとめ

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

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

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

外部リンク
・ 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 

# 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_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 generate_html1( body0 ): 
    str1 = '''
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
{body1} 
</body>
</html>
'''.format( body1 = body0 ) 
    return str1 

def show_xmls1( xml1 ): 
    show_xml0( xml1 )                    # element rss 
    show_xml0( xml1[0] )                 # element channel 
    show_xml0( xml1[0][0] )              # element title 
    show_xml0( xml1[0][6] )              # element item, index 6 
    show_xml0( xml1[0][6][0] )           # element title 
    show_xml0( xml1[0][6][1] )           # element link 

def get_weather_forecast1( xml1 ): 
    str1 = "" 
    for i1 in range( 8 ): 
        str2 = xml1[0][6+i1][0].text     # weather forecast 
        str1 = str1 + str2 + "\n" 
    return str1 

def get_weather_forecast2( xml1 ):       # html 
    str1 = "" 
    for i1 in range( 8 ): 
        str2 = xml1[0][6+i1][0].text     # weather forecast 
        str3 = xml1[0][6+i1][1].text     # url 
        str1 = str1 + '<a href="' + str3 + '" target="weather1">' + str2 + '</a><br>' + '\n' 
    str2 = generate_html1( str1 ) 
    return str2 

# https://weather.yahoo.co.jp/weather/rss/
url1 = "https://rss-weather.yahoo.co.jp/rss/days/4410.xml"        # tokyo, Japan 

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 ) 

str2 = get_weather_forecast2( xml1 )     # html 
print( str2 ) 

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


 

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