Python

【5年分の気象データを取得!】python・Beautifulsoupを使って気象庁データをスクレイピングする方法!実践活用!

pythonを使用してスクレイピングする方法を紹介します。今回は実際に使用できるように5年分の気象データを取得する方法を紹介します!

実際に私がこれから新規就農するということがあり、最近の異常気象の影響などを考慮して農業をする必要があると感じました。そのため、過去の気象データをスクレイピングしたいということを感じましたので、スクレイピングする方法を紹介します。
気象庁には過去1970年代からの 気象データがwebに公開されているため、これを活用する方法を紹介します。

アイコン名を入力

各都道府県の過去の気象データを取得して、そのファイルをもとに携帯から今年の気象状況は通常か異常かを把握できるようにしたい!

だって新規就農するから!

アイコン名を入力

それじゃあ、まずはデータを取得する方法を紹介しよう!
データを取得すれば、携帯からアクセスして表示することができるよ!

アイコン名を入力

どうやったらできる?

アイコン名を入力

そんな時はスクレイピングをしてWebからデータを取得するのだ!
では実際にやってみよう!

ということで、今回はスクレイピングをして過去5年分の気象データを取得する方法を紹介します。

記事の対象者

・スクレイピングを使用するプログラムを知りたい
・Pythonを使用して、データ分析をしてみたい
・気象データを活用したい

こんな人に紹介します。

では早速説明します。動画でも紹介しているので参照して下さい。

https://youtu.be/H4eaw2M_Qko

気象庁データはこちらから確認できます。

https://www.data.jma.go.jp/stats/etrn/index.php

🚀 0円で現役エンジニアから学べる【Techスクールオンライン】のお申込みをお勧めします。 このオンラインスクールでは、現役のエンジニアから直接学ぶことができ、プログラミングの基礎から高度なスキルまでを習得できます。しかも、今なら 0円 で受講できるチャンスです。 エンジニア転職を考えている方やプログラミングに興味がある方、新しいスキルを習得したい方に特におすすめです。

プログラム内容

ではまず、プログラムの全文を早速載せます。コメント文に内容の説明は書いてあるの見てみてください。

まずコードの構成は、下記です。

・プログラムコード(test.ipynb)
・都市コード(city.csv)

city.csvに都市コードが記載されているのでこのコードがbase_urlに追加され、気象庁データにアクセスできるようになっております。

base_urlはこれ”https://www.data.jma.go.jp/stats/etrn/”

import requests
from bs4 import BeautifulSoup
import pandas as pd
from urllib.parse import urljoin

# ベースURL
base_url = "https://www.data.jma.go.jp/stats/etrn/"
start_url = base_url

# 取得する年度をリストとして設定
years = [2022, 2023, 2024]

# データを格納するリスト
all_data = []

# 各年のデータを取得
for year in years:
    # ステップ1: ベースURLにアクセス
    response = requests.get(start_url)
    response.encoding = response.apparent_encoding
    html = response.text

    # BeautifulSoupを使ってHTMLを解析
    soup = BeautifulSoup(html, 'html.parser')

    # ステップ2: "地点の選択をクリア" リンクを見つける
    clear_link_tag = soup.find('a', string='地点の選択をクリア')
    if clear_link_tag:
        clear_link_url = urljoin(base_url, clear_link_tag['href'])
        response = requests.get(clear_link_url)
        response.encoding = response.apparent_encoding
        html = response.text
        soup = BeautifulSoup(html, 'html.parser')

        # ステップ3: "都府県・地方を選択" リンクを見つける
        prefecture_link_tag = soup.find('a', href="select/prefecture00.php?prec_no=&block_no=&year=&month=&day=&view=")
        print(prefecture_link_tag)
        if prefecture_link_tag:
            prefecture_link_url = urljoin(base_url, prefecture_link_tag['href'])
            response = requests.get(prefecture_link_url)
            response.encoding = response.apparent_encoding
            html = response.text
            soup = BeautifulSoup(html, 'html.parser')

            # ステップ4: "静岡県" リンクを見つける
            shizuoka_link_tag = soup.find('area', alt="静岡県")
            if shizuoka_link_tag:
                shizuoka_link_url = urljoin(prefecture_link_url, shizuoka_link_tag['href'])
                response = requests.get(shizuoka_link_url)
                response.encoding = response.apparent_encoding
                html = response.text
                soup = BeautifulSoup(html, 'html.parser')

                # ステップ5: "静岡市" リンクを見つける
                shizuoka_city_link_tag = soup.find('area', alt="静岡")
                if shizuoka_city_link_tag:
                    shizuoka_city_link_url = urljoin(shizuoka_link_url, shizuoka_city_link_tag['href'])
                    response = requests.get(shizuoka_city_link_url)
                    response.encoding = response.apparent_encoding
                    html = response.text
                    soup = BeautifulSoup(html, 'html.parser')

                    # ステップ6: "2024年" リンクを見つける
                    year_link_tag = soup.find('a', href=f"index.php?prec_no=50&block_no=47656&year={year}&month=&day=&view=")
                    if year_link_tag:
                        year_link_url = urljoin(shizuoka_city_link_url, year_link_tag['href'])
                        response = requests.get(year_link_url)
                        response.encoding = response.apparent_encoding
                        html = response.text
                        soup = BeautifulSoup(html, 'html.parser')

                        # ステップ7: "2024年の月ごとの値を表示" リンクを見つける
                        monthly_link_tag = soup.find('a', href=f"view/monthly_s1.php?prec_no=50&block_no=47656&year={year}&month=&day=&view=")
                        if monthly_link_tag:
                            monthly_link_url = urljoin(year_link_url, monthly_link_tag['href'])
                            response = requests.get(monthly_link_url)
                            response.encoding = response.apparent_encoding
                            html = response.text
                            soup = BeautifulSoup(html, 'html.parser')

                            # テーブルを見つけてDataFrameを作成
                            table = soup.find('table', {'id': 'tablefix1'})

                            # ヘッダー行を処理
                            headers = [
                                "年", "月", "気圧(hPa)_現地", "気圧(hPa)_海面", "降水量(mm)_合計",
                                "降水量(mm)_最大_日", "降水量(mm)_最大_1時間", "降水量(mm)_最大_10分間",
                                "気温(℃)_平均_日平均", "気温(℃)_平均_日最高", "気温(℃)_平均_日最低",
                                "気温(℃)_最高", "気温(℃)_最低", "湿度(%)_平均", "湿度(%)_最小",
                                "風向・風速(m/s)_平均風速", "風向・風速(m/s)_最大風速_風速",
                                "風向・風速(m/s)_最大風速_風向", "風向・風速(m/s)_最大瞬間風速_風速",
                                "風向・風速(m/s)_最大瞬間風速_風向", "降雪(cm)_合計", "降雪(cm)_日合計の最大",
                                "降雪(cm)_最深積雪", "日照時間(h)", "全天日射量(MJ/㎡)", 
                                "雲量_平均", "大気現象_雪日数", "大気現象_霧日数", "大気現象_雷日数"
                            ]

                            # データ行を処理
                            rows = []
                            for tr in table.find_all('tr')[3:]:
                                cells = [td.get_text(strip=True) for td in tr.find_all('td')]
                                if len(cells) == len(headers) - 1:  # 年を追加するため-1
                                    rows.append([year] + cells)

                            # データを全体リストに追加
                            all_data.extend(rows)

# 全データをDataFrameに変換
df = pd.DataFrame(all_data, columns=headers)

# 空の行を削除
df = df.replace('', pd.NA).dropna(how='all')

# DataFrameをCSVファイルに保存
csv_filename = "shizuoka_weather_2022_2024.csv"
df.to_csv(csv_filename, index=False, encoding='utf-8-sig')
print(f"Data saved to {csv_filename}")

city.csvがこれ。都市コードで少し間違っているものもありますが、気象庁データから修正してください。(もし取得できなかった場合)

prefecture,city,link
青森県,青森,prec_no=31&block_no=47575
岩手県,盛岡,prec_no=32&block_no=47582
宮城県,仙台,prec_no=34&block_no=47590
秋田県,秋田,prec_no=33&block_no=47582
山形県,山形,prec_no=35&block_no=47588
福島県,福島,prec_no=36&block_no=47595
茨城県,水戸,prec_no=40&block_no=47629
栃木県,宇都宮,prec_no=41&block_no=47615
群馬県,前橋,prec_no=42&block_no=47624
埼玉県,さいたま,prec_no=43&block_no=47626
千葉県,千葉,prec_no=45&block_no=47682
東京都,東京,prec_no=44&block_no=47662
神奈川県,横浜,prec_no=46&block_no=47670
新潟県,新潟,prec_no=54&block_no=47604
富山県,富山,prec_no=55&block_no=47607
石川県,金沢,prec_no=56&block_no=47605
福井県,福井,prec_no=57&block_no=47616
山梨県,甲府,prec_no=49&block_no=47638
長野県,長野,prec_no=48&block_no=47610
岐阜県,岐阜,prec_no=50&block_no=47632
静岡県,静岡,prec_no=49&block_no=47656
愛知県,名古屋,prec_no=51&block_no=47636
三重県,津,prec_no=52&block_no=47651
滋賀県,大津,prec_no=60&block_no=47759
京都府,京都,prec_no=61&block_no=47759
大阪府,大阪,prec_no=62&block_no=47772
兵庫県,神戸,prec_no=63&block_no=47770
奈良県,奈良,prec_no=64&block_no=47774
和歌山県,和歌山,prec_no=65&block_no=47777
鳥取県,鳥取,prec_no=66&block_no=47746
島根県,松江,prec_no=67&block_no=47741
岡山県,岡山,prec_no=68&block_no=47768
広島県,広島,prec_no=69&block_no=47765
山口県,山口,prec_no=70&block_no=47784
徳島県,徳島,prec_no=71&block_no=47890
香川県,高松,prec_no=72&block_no=47887
愛媛県,松山,prec_no=73&block_no=47891
高知県,高知,prec_no=74&block_no=47893
福岡県,福岡,prec_no=75&block_no=47807
佐賀県,佐賀,prec_no=76&block_no=47813
長崎県,長崎,prec_no=77&block_no=47817
熊本県,熊本,prec_no=78&block_no=47819
大分県,大分,prec_no=79&block_no=47815
宮崎県,宮崎,prec_no=80&block_no=47827
鹿児島県,鹿児島,prec_no=81&block_no=47827
沖縄県,那覇,prec_no=82&block_no=47936

このcity.csvファイルを作成しておきます。(weather_data_2020_2024.csvはあとから作成されます。)

実際にプログラムを回すと、ファイルが生成され、下記のようなデータが保存されます。

大量のデータが保存されます。

以上です。

プログラムの説明

少しプログラムの説明をします。

スクレイピングプログラムの説明

import requests from bs4 import BeautifulSoup import pandas as pd import time

まず最初に、必要なライブラリをインポートしています。

  • requestsはウェブページの情報を取得するために使います。
  • BeautifulSoupは取得したHTMLを解析するためのライブラリです。
  • pandasはデータを扱うための便利なライブラリです。
  • timeは処理の間に待機時間を入れるために使います。

base_url = "https://www.data.jma.go.jp/obd/stats/etrn/"

次に、気象庁のデータを取得するためのベースとなるURLを設定しています。

city_df = pd.read_csv('city.csv')

ここでは、都市の情報が入ったcity.csvというファイルを読み込んでいます。このファイルには、都道府県や都市名、リンクなどが入っています。

years = [2020, 2021, 2022, 2023, 2024]

次に、取得したい年をリストで指定しています。今回は2020年から2024年までの5年間です。自由に変更可能です。

all_data = []

ここでは、取得したデータを全て入れるための空のリストを用意しています。

# 各都市に対してスクレイピングを実行
for index, row in city_df.iterrows():
    prefecture = row['prefecture']
    city = row['city']
    link = row['link']
    print(f"スクレイピング中: {prefecture} - {city}")

ここからは、読み込んだ都市ごとにスクレイピングを行います。

  • city_df.iterrows()で、都市データを一行ずつ取り出します。
  • prefecturecitylinkにそれぞれ都道府県、都市名、リンクの情報を入れます。
  • どの都市をスクレイピングしているかを表示しています。
    for year in years:
        try:
            # 対象年の月ごとのデータのURLを生成
            monthly_url = f"{base_url}view/monthly_s1.php?{link}&year={year}&month=&day=&view="
            response = requests.get(monthly_url)
            response.encoding = response.apparent_encoding
            html = response.text

次に、指定した年ごとにデータを取得します。

  • monthly_urlで、その年のデータのURLを作成しています。
  • requests.get()でそのURLのページを取得します。
  • 文字エンコーディングを設定し、HTMLのテキストを取得します。

            # BeautifulSoupを使ってHTMLを解析
            soup = BeautifulSoup(html, 'html.parser')

取得したHTMLをBeautifulSoupで解析できるようにします。

            # データが存在するか確認
            if "該当するデータは存在しません" in html:
                print(f"{prefecture} - {city} の {year} 年のデータが存在しません。")
                continue

ここでは、その年のデータが存在するかをチェックしています。もしデータがなければ、その都市と年を表示して次に進みます。

            # テーブルを見つけてDataFrameを作成
            table = soup.find('table', {'id': 'tablefix1'})

HTMLの中から、データが入っているテーブルを探します。

            if table:
                # ヘッダー行を処理
                headers = [
                    "都市名", "年", "月", "平均気圧(hPa)_現地", "平均気圧(hPa)_海面",
                    "降水量(mm)_合計", "降水量(mm)_最大1日", "降水量(mm)_最大1時間",
                    "降水量(mm)_最大10分間", "平均気温(℃)", "最高気温(℃)",
                    "最低気温(℃)", "最高気温(℃)_極値", "最低気温(℃)_極値",
                    "平均湿度(%)", "最小湿度(%)", "平均風速(m/s)", "最大風速(m/s)",
                    "最大風速(m/s)_風向", "最大瞬間風速(m/s)", "最大瞬間風速(m/s)_風向",
                    "降雪(cm)_合計", "降雪(cm)_最大1日", "最深積雪(cm)",
                    "日照時間(h)", "全天日射量(MJ/㎡)", "平均雲量(10分比)",
                    "雪日数(日)", "霧日数(日)", "雷日数(日)"
                ]

テーブルのカラム(列)の名前をリストで定義しています。ほしいデータを記載しています

                # データ行を処理
                rows = []
                for tr in table.find_all('tr')[4:]:
                    cells = [td.get_text(strip=True) for td in tr.find_all('td')]
                    if len(cells) >= len(headers) - 2:
                        row_data = [city, year] + cells[:len(headers) - 2]
                        rows.append(row_data)

テーブルのデータ部分を取得しています。

  • table.find_all('tr')[4:]で、ヘッダー以降の行を取得しています。
  • 各行のセルのテキストをリストにします。
  • データの長さがヘッダーと合っているか確認し、都市名と年を追加してrowsリストに入れています。
                # データを全体リストに追加
                all_data.extend(rows)

取得したデータをall_dataリストに追加しています。

最終的にデータをCSVファイルに保存しています。これで、過去4年間(2020年から2024年まで)のデータが取得できました。

このように簡単にwebからスクレイピングできるのでやってみてください。

一応グラフを表示するプログラムはこちらになります。

グラフを表示するプログラム

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm

# 先ほど保存したCSVファイルを読み込む
csv_filename = "shizuoka_weather_2022_2024.csv"
df = pd.read_csv(csv_filename)

# データ型を確認して、必要に応じて変換する
print(df.dtypes)
df['気温(℃)_平均_日平均'] = pd.to_numeric(df['気温(℃)_平均_日平均'], errors='coerce')
df['湿度(%)_最小'] = pd.to_numeric(df['湿度(%)_最小'], errors='coerce')
df['月'] = pd.to_numeric(df['月'], errors='coerce')

# 日本語フォントの設定
# フォントファイルのパスを指定してください。例えば、Noto Sans JP フォントを使用します。
font_path = 'C:/Windows/Fonts/meiryo.ttc'  # 適切なパスを指定
jp_font = fm.FontProperties(fname=font_path)

# グラフを描画する
fig, ax1 = plt.subplots()

# 各年のデータをプロットする
for year in df['年'].unique():
    df_year = df[df['年'] == year]
    ax1.plot(df_year['月'], df_year['気温(℃)_平均_日平均'], marker='o', label=f'{year} 気温')
    ax1.set_ylabel('気温 (℃)', fontproperties=jp_font)
    ax1.set_xlabel('月', fontproperties=jp_font)

ax2 = ax1.twinx()
for year in df['年'].unique():
    df_year = df[df['年'] == year]
    ax2.plot(df_year['月'], df_year['湿度(%)_最小'], marker='x', linestyle='--', label=f'{year} 湿度')
    ax2.set_ylabel('湿度 (%)', fontproperties=jp_font)

fig.legend(loc='upper right', prop=jp_font)
plt.title('月ごとの気温と湿度 (2022-2024)', fontproperties=jp_font)
plt.show()

実行するとこんなグラフが表示されます。

🚀 0円で現役エンジニアから学べる【Techスクールオンライン】のお申込みをお勧めします。 このオンラインスクールでは、現役のエンジニアから直接学ぶことができ、プログラミングの基礎から高度なスキルまでを習得できます。しかも、今なら 0円 で受講できるチャンス。
私がツナグバに登録してから、求人情報が豊富に届き、自分に合った仕事を見つけることができました。特に、第二新卒向けの求人情報が多いので、自分のスキルや経験を活かしながら新たなキャリアに挑戦することができました。転職活動は不安も多いですが、ツナグバのサポートがあれば、成功への道が明るく感じました。