Pythonで気象警報・注意報を取得する【気象庁防災情報XML】

これは、[防災アプリ開発 Advent Calendar 2023](https://adventar.org/calendars/9301)の19日目の記事です。
昨日は、[のたさんの記事](https://qiita.com/oruponu/items/817d949f944e090b45cb)でした。

# はじめに
こんにちは、りーちゃんです。
Twitterでは公言していませんが、ここ数か月地震観測はやっていません。(めんどくなってしまいました、、)
いまのところ実質的な引退といったところでしょうかね。

最近は、ちょいちょいDiscord Bot(Python/discord.py)のプログラムを触っています。

さて、今回はPythonで気象警報・注意報を取得する方法について書いていこうと思います。

:::note warn
この記事では、Pythonをあまり触っていない方でも比較的理解しやすいように書いておりますが、誤った情報を書いてしまっている可能性はあります。
また、プログラムは冗長になってしまっている可能性はあります。
あらかじめご了承ください。間違いがありましたら、お知らせいただくと大変助かります。
:::
# 概要
この記事では、気象庁防災情報XML(以下、JMAXML)を使用して気象警報・注意報を取得していきたいと思います。

https://xml.kishou.go.jp/index.html


# この記事を書くにあたっての環境
- `Windows 10 Home 64bit ver.22H2(OS build: 19045.3693)`
- `Python 3.11.3`
- `pip 23.3.2`
- `requests 2.31.0`
- `beautifulsoup4　4.12.2`
- `xmltodict 0.13.0`


# 1. 必要なパッケージをインストールする
気象警報・注意報を取得するためには、追加で下記パッケージが必要になります。
- `requests`
- `beautifulsoup4`
- `xmltodict`

コマンドプロンプトなどを起動して、pipを使用して上記パッケージをインストールしてください。

```shell-session
$ pip install <package-name>
```


# 2. 取得したい市町村を決める
気象警報・注意報を取得するためには、気象台名が必要になります。
コードを書く前に、まずは取得したい市町村を決めて、その市町村がどこの気象台なのかを調べましょう。

この記事では、`石川県金沢市`の気象警報・注意報を取得していくことにします。

## どこの気象台なのかを調べる
下記の表を参照に、どの気象台なのか調べていきます。

:::note warn
下記の表は、JMAのサイトなどを参考に自分が自作したものです。
自作のため、誤りがある可能性はあります。あらかじめご了承ください。
:::

```py:北海道、鹿児島県、沖縄県 以外の都道府県
    '宮城県': '仙台管区気象台',
    '青森県': '青森地方気象台',
    '岩手県': '盛岡地方気象台',
    '秋田県': '秋田地方気象台',
    '山形県': '山形地方気象台',
    '福島県': '福島地方気象台',
    '東京都': '気象庁',
    '茨城県': '水戸地方気象台',
    '栃木県': '宇都宮地方気象台',
    '群馬県': '前橋地方気象台',
    '埼玉県': '熊谷地方気象台',
    '千葉県': '銚子地方気象台',
    '神奈川県': '横浜地方気象台',
    '山梨県': '甲府地方気象台',
    '長野県': '長野地方気象台',
    '新潟県': '新潟地方気象台',
    '富山県': '富山地方気象台',
    '石川県': '金沢地方気象台',
    '福井県': '福井地方気象台',
    '愛知県': '名古屋地方気象台',
    '岐阜県': '岐阜地方気象台',
    '静岡県': '静岡地方気象台',
    '三重県': '津地方気象台',
    '大阪府': '大阪管区気象台',
    '滋賀県': '彦根地方気象台',
    '京都府': '京都地方気象台',
    '兵庫県': '神戸地方気象台',
    '奈良県': '奈良地方気象台',
    '和歌山県': '和歌山地方気象台',
    '広島県': '広島地方気象台',
    '鳥取県': '鳥取地方気象台',
    '島根県': '松江地方気象台',
    '岡山県': '岡山地方気象台',
    '香川県': '高松地方気象台',
    '徳島県': '徳島地方気象台',
    '愛媛県': '松山地方気象台',
    '高知県': '高知地方気象台',
    '福岡県': '福岡管区気象台',
    '山口県': '下関地方気象台',
    '佐賀県': '佐賀地方気象台',
    '長崎県': '長崎地方気象台',
    '熊本県': '熊本地方気象台',
    '大分県': '大分地方気象台',
    '宮崎県': '宮崎地方気象台',
```

<details><summary>北海道</summary>

```py
    '稚内市': '稚内地方気象台',
    '猿払村': '稚内地方気象台',
    '豊富町': '稚内地方気象台',
    '幌延町': '稚内地方気象台',
    '浜頓別町': '稚内地方気象台',
    '中頓別町': '稚内地方気象台',
    '枝幸町': '稚内地方気象台',
    '礼文町': '稚内地方気象台',
    '利尻町': '稚内地方気象台',
    '利尻富士町': '稚内地方気象台',
    '士別市': '旭川地方気象台',
    '名寄市': '旭川地方気象台',
    '和寒町': '旭川地方気象台',
    '剣淵町': '旭川地方気象台',
    '下川町': '旭川地方気象台',
    '美深町': '旭川地方気象台', 
    '音威子府村': '旭川地方気象台',
    '中川町': '旭川地方気象台',
    '幌加内町': '旭川地方気象台',
    '旭川市': '旭川地方気象台',
    '鷹栖町': '旭川地方気象台',
    '東神楽町': '旭川地方気象台',
    '当麻町': '旭川地方気象台',
    '比布町': '旭川地方気象台',
    '愛別町': '旭川地方気象台',
    '上川町': '旭川地方気象台',
    '東川町': '旭川地方気象台',
    '美瑛町': '旭川地方気象台',
    '富良野市': '旭川地方気象台',
    '上富良野町': '旭川地方気象台',
    '中富良野町': '旭川地方気象台',
    '南富良野町': '旭川地方気象台',
    '占冠村': '旭川地方気象台',
    '遠別町': '旭川地方気象台',
    '天塩町': '旭川地方気象台',
    '苫前町': '旭川地方気象台',
    '羽幌町': '旭川地方気象台',
    '天売焼尻': '旭川地方気象台',
    '初山別村': '旭川地方気象台',
    '石狩市': '札幌管区気象台',
    '当別町': '札幌管区気象台',
    '新篠津村': '札幌管区気象台',
    '札幌市': '札幌管区気象台',
    '江別市': '札幌管区気象台',
    '千歳市': '札幌管区気象台',
    '恵庭市': '札幌管区気象台',
    '北広島市': '札幌管区気象台',
    '深川市': '札幌管区気象台',
    '妹背牛町': '札幌管区気象台',
    '秩父別町': '札幌管区気象台',
    '北竜町': '札幌管区気象台',
    '沼田町': '札幌管区気象台',
    '芦別市': '札幌管区気象台',
    '赤平市': '札幌管区気象台',
    '滝川市': '札幌管区気象台',
    '砂川市': '札幌管区気象台',
    '歌志内市': '札幌管区気象台',
    '奈井江町': '札幌管区気象台',
    '上砂川町': '札幌管区気象台',
    '浦臼町': '札幌管区気象台',
    '新十津川町': '札幌管区気象台',
    '雨竜町': '札幌管区気象台',
    '夕張市': '札幌管区気象台',
    '岩見沢市': '札幌管区気象台',
    '美唄市': '札幌管区気象台',
    '三笠市': '札幌管区気象台',
    '南幌町': '札幌管区気象台',
    '由仁町': '札幌管区気象台',
    '長沼町': '札幌管区気象台',
    '栗山町': '札幌管区気象台',
    '月形町': '札幌管区気象台',
    '小樽市': '札幌管区気象台',
    '積丹町': '札幌管区気象台',
    '古平町': '札幌管区気象台',
    '仁木町': '札幌管区気象台',
    '余市町': '札幌管区気象台',
    '赤井川村': '札幌管区気象台',
    'ニセコ町': '札幌管区気象台',
    '真狩村': '札幌管区気象台',
    '留寿都村': '札幌管区気象台',
    '喜茂別町': '札幌管区気象台',
    '京極町': '札幌管区気象台',
    '倶知安町': '札幌管区気象台',
    '島牧村': '札幌管区気象台',
    '寿都町': '札幌管区気象台',
    '黒松内町': '札幌管区気象台',
    '蘭越町': '札幌管区気象台',
    '共和町': '札幌管区気象台',
    '岩内町': '札幌管区気象台',
    '泊村': '札幌管区気象台',
    '神恵内村': '札幌管区気象台',
    '北見市常呂': '網走地方気象台',
    '網走市': '網走地方気象台',
    '佐呂間町': '網走地方気象台',
    '大空町': '網走地方気象台',
    '斜里町': '網走地方気象台',
    '清里町': '網走地方気象台',
    '小清水町': '網走地方気象台',
    '美幌町': '網走地方気象台',
    '津別町': '網走地方気象台',
    '北見市北見': '網走地方気象台',
    '訓子府町': '網走地方気象台',
    '置戸町': '網走地方気象台',
    '紋別市': '網走地方気象台',
    '滝上町': '網走地方気象台',
    '興部町': '網走地方気象台',
    '西興部村': '網走地方気象台',
    '雄武町': '網走地方気象台',
    '遠軽町': '網走地方気象台',
    '湧別町': '網走地方気象台',
    '弟子屈町': '釧路地方気象台',
    '釧路市阿寒': '釧路地方気象台',
    '標茶町': '釧路地方気象台',
    '鶴居村': '釧路地方気象台',
    '厚岸町': '釧路地方気象台',
    '浜中町': '釧路地方気象台',
    '釧路市釧路': '釧路地方気象台',
    '釧路市音別': '釧路地方気象台',
    '釧路町': '釧路地方気象台',
    '白糠町': '釧路地方気象台',
    '中標津町': '釧路地方気象台',
    '標津町': '釧路地方気象台',
    '羅臼町': '釧路地方気象台',
    '別海町': '釧路地方気象台',
    '根室市': '釧路地方気象台',
    '上士幌町': '帯広測候所',
    '鹿追町': '帯広測候所',
    '新得町': '帯広測候所',
    '足寄町': '帯広測候所',
    '陸別町': '帯広測候所',
    '帯広市': '帯広測候所',
    '音更町': '帯広測候所',
    '士幌町': '帯広測候所',
    '清水町': '帯広測候所',
    '芽室町': '帯広測候所',
    '幕別町': '帯広測候所',
    '池田町': '帯広測候所',
    '豊頃町': '帯広測候所',
    '本別町': '帯広測候所',
    '浦幌町': '帯広測候所',
    '中札内村': '帯広測候所',
    '更別村': '帯広測候所',
    '大樹町': '帯広測候所',
    '広尾町': '帯広測候所',
    '伊達市伊達': '室蘭地方気象台',
    '伊達市大滝': '室蘭地方気象台',
    '豊浦町': '室蘭地方気象台',
    '壮瞥町': '室蘭地方気象台',
    '洞爺湖町': '室蘭地方気象台',
    '室蘭市': '室蘭地方気象台',
    '苫小牧市': '室蘭地方気象台',
    '登別市': '室蘭地方気象台',
    '白老町': '室蘭地方気象台',
    '厚真町': '室蘭地方気象台',
    '安平町': '室蘭地方気象台',
    'むかわ町': '室蘭地方気象台',
    '日高町日高': '室蘭地方気象台',
    '日高町門別': '室蘭地方気象台',
    '平取町': '室蘭地方気象台',
    '新冠町': '室蘭地方気象台',
    '新ひだか町': '室蘭地方気象台',
    '浦河町': '室蘭地方気象台',
    '様似町': '室蘭地方気象台',
    'えりも町': '室蘭地方気象台',
    '八雲町八雲': '函館地方気象台',
    '長万部町': '函館地方気象台',
    '函館市': '函館地方気象台',
    '北斗市': '函館地方気象台',
    '七飯町': '函館地方気象台',
    '鹿部町': '函館地方気象台',
    '森町': '函館地方気象台',
    '松前町': '函館地方気象台',
    '福島町': '函館地方気象台',
    '知内町': '函館地方気象台',
    '木古内町': '函館地方気象台',
    '八雲町熊石': '函館地方気象台',
    '今金町': '函館地方気象台',
    'せたな町': '函館地方気象台',
    '江差町': '函館地方気象台',
    '上ノ国町': '函館地方気象台',
    '厚沢部町': '函館地方気象台',
    '乙部町': '函館地方気象台',
    '奥尻町': '函館地方気象台'
```
</details>

<details><summary>鹿児島県</summary>

```py
    '鹿児島市': '鹿児島地方気象台',
    '日置市': '鹿児島地方気象台',
    'いちき串木野市': '鹿児島地方気象台',
    '阿久根市': '鹿児島地方気象台',
    '出水市': '鹿児島地方気象台',
    '伊佐市': '鹿児島地方気象台',
    '長島町': '鹿児島地方気象台',
    '薩摩川内市': '鹿児島地方気象台',
    '霧島市': '鹿児島地方気象台',
    '姶良市': '鹿児島地方気象台',
    'さつま町': '鹿児島地方気象台',
    '湧水町': '鹿児島地方気象台',
    '薩摩川内市甑島': '鹿児島地方気象台',
    '枕崎市': '鹿児島地方気象台',
    '指宿市': '鹿児島地方気象台',
    '南さつま市': '鹿児島地方気象台',
    '南九州市': '鹿児島地方気象台',
    '曽於市': '鹿児島地方気象台',
    '志布志市': '鹿児島地方気象台',
    '大崎町': '鹿児島地方気象台',
    '鹿屋市': '鹿児島地方気象台',
    '垂水市': '鹿児島地方気象台',
    '東串良町': '鹿児島地方気象台',
    '錦江町': '鹿児島地方気象台',
    '南大隅町': '鹿児島地方気象台',
    '肝付町': '鹿児島地方気象台',
    '西之表市': '鹿児島地方気象台',
    '三島村': '鹿児島地方気象台',
    '中種子町': '鹿児島地方気象台',
    '南種子町': '鹿児島地方気象台',
    '屋久島町': '鹿児島地方気象台',
    '奄美市': '名瀬測候所',
    '大和村': '名瀬測候所',
    '宇検村': '名瀬測候所',
    '瀬戸内町': '名瀬測候所',
    '龍郷町': '名瀬測候所',
    '喜界町': '名瀬測候所',
    '徳之島町': '名瀬測候所',
    '天城町': '名瀬測候所',
    '伊仙町': '名瀬測候所',
    '和泊町': '名瀬測候所',
    '知名町': '名瀬測候所',
    '与論町': '名瀬測候所',
    '十島村': '名瀬測候所'
```
</details>

<details><summary>沖縄県</summary>

```py
    '那覇市': '沖縄気象台',
    '浦添市': '沖縄気象台',
    '糸満市': '沖縄気象台',
    '豊見城市': '沖縄気象台',
    '南城市': '沖縄気象台',
    '西原町': '沖縄気象台',
    '与那原町': '沖縄気象台',
    '南風原町': '沖縄気象台',
    '八重瀬町': '沖縄気象台',
    '宜野湾市': '沖縄気象台',
    '沖縄市': '沖縄気象台',
    'うるま市': '沖縄気象台',
    '読谷村': '沖縄気象台',
    '嘉手納町': '沖縄気象台',
    '北谷町': '沖縄気象台',
    '北中城村': '沖縄気象台',
    '中城村': '沖縄気象台',
    '渡嘉敷村': '沖縄気象台',
    '座間味村': '沖縄気象台',
    '粟国村': '沖縄気象台',
    '渡名喜村': '沖縄気象台',
    '伊平屋村': '沖縄気象台',
    '是名村': '沖縄気象台',
    '国頭村': '沖縄気象台',
    '大宜味村': '沖縄気象台',
    '東村': '沖縄気象台',
    '名護市': '沖縄気象台',
    '今帰仁村': '沖縄気象台',
    '本部町': '沖縄気象台',
    '伊江村': '沖縄気象台',
    '恩納村': '沖縄気象台',
    '宜野座村': '沖縄気象台',
    '金武町': '沖縄気象台',
    '久米島町': '沖縄気象台',
    '南大東村': '南大東島地方気象台',
    '北大東村': '南大東島地方気象台',
    '宮古島市': '宮古島地方気象台',
    '多良間村': '宮古島地方気象台',
    '石垣市': '石垣島地方気象台',
    '竹富町': '石垣島地方気象台',
    '与那国町': '石垣島地方気象台'
```
</details>

というわけで、`石川県`の気象台は`金沢地方気象台`だということが分かりました。


# 3. データを取得する
気象台が分かったところで、実際に取得していきましょう。

JMAXMLで提供されているデータは数多くあります。

https://xml.kishou.go.jp/jmaxml_20221209_format_v1_3_hyo1_1.pdf

上記ページに、提供されているデータの一覧が載っています。
今回はこのうちの、`VPWW53 (気象特別警報・警報・注意報)`という種類のXMLを取得していきます。

## 大元のXMLを取得する
JMAXMLは、さまざまな種類のデータをとりまとめた大元のXMLファイルがあり、その中にそれぞれのXMLデータのURLが格納されている という構造になっています。
まずは、この大元のXMLを取得する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3064849/e5ca954b-32a6-45df-7c52-662bd560bc02.png)
[気象庁防災情報XMLフォーマット形式電文の公開（PULL型）](https://xml.kishou.go.jp/xmlpull.html)の`Atomフィード` > `長期フィード` > `随時`のXMLが大元のデータになりますので、これを取得していきます。
下記のコードを記述してください。

```py:大元のXMLを取得
#パッケージをインポートする
import requests
from bs4 import BeautifulSoup
import xmltodict

## 大元のXMLのURLを指定
feed_url = 'https://www.data.jma.go.jp/developer/xml/feed/extra_l.xml'
#リクエスト(データを取得する)
feed_xml = requests.get(feed_url)
feed_soup = str(BeautifulSoup(feed_xml.content, "xml"))
#取得したXMLを、辞書型に変換する
feed_dict = xmltodict.parse(feed_soup)
```

これで、大元のXMLが取得できました。
取得したデータは、下記のような形式となります。

```javascript:feed_dictの中身
{
    'feed': {
        '@lang': 'ja',
        '@xmlns': 'http://www.w3.org/2005/Atom',
        'title': '長期（随時）',
        'subtitle': 'JMAXML publishing feed',
        'updated': '2023-12-20T22:10:58+09:00',
        'id': 'https://www.data.jma.go.jp/developer/xml/feed/extra.xml#long_1703077858',
        'link': [
            {
                '@href': 'https://www.jma.go.jp/',
                '@rel': 'related'
            },
            {
                '@href': 'https://www.data.jma.go.jp/developer/xml/feed/extra.xml',
                '@rel': 'self'
            },
            {
                '@href': 'http://alert-hub.appspot.com/',
                '@rel': 'hub'
            }
        ],
        'rights': {
            '@type': 'html',
            '#text': '<a href="https://www.jma.go.jp/jma/kishou/info/coment.html">利用規約</a>,\n<a href="https://www.jma.go.jp/jma/en/copyright.html">Terms of Use</a>'
        },
        'entry': [
            {
            
                ...中略...
            
            },
            {
                'title': '気象特別警報・警報・注意報',
                'id': 'https://www.data.jma.go.jp/developer/xml/data/20231220123714_0_VPWW53_170000.xml',
                'updated': '2023-12-20T12:37:14Z',
                'author': {
                    'name': '金沢地方気象台'
                },
                'link': {
                    '@href': 'https://www.data.jma.go.jp/developer/xml/data/20231220123714_0_VPWW53_170000.xml',
                    '@type': 'application/xml'
                },
                'content': {
                    '@type': 'text',
                    '#text': '【石川県気象警報・注意報】石川県では、２１日明け方から２１日夜遅くまで高波に警戒してください。'
                }
            },
            {
            
                ...中略...
            
            },


            ...中略...

        ]
    }
}
```

## 気象警報・注意報のXMLを取得する
大元のXMLが取得できたので、`金沢地方気象台`の一番最新の`VPWW53`を取得していきます。

まずは、先述の`feed_dict`の中から`金沢地方気象台`の`VPWW53`が含まれるURLを探していきます。
そうすると、

`feed` → `entry` → n個目 → `id`

にURLがあることが分かります。

このことを踏まえると、下記のように記述することになります。
先述のコードのあとに、下記のコードを記述してください。

```py:金沢地方気象台の最新のVPWW53を取得
#気象台と市町村名を指定
kisyodai = '金沢地方気象台'
city = '金沢市'

#XMLの中の'entry'の数だけ繰り返す
for i in range(len(feed_dict["feed"]["entry"])):

    #'VPWW53'が含まれるURLが見つかったら
    if 'VPWW53' in feed_dict["feed"]["entry"][i]["id"]:
    
        #'金沢地方気象台'が見つかったら
        if  kisyodai == feed_dict["feed"]["entry"][i]["author"]["name"]:
            #リクエスト(データを取得する)
            VPWW53_xml = requests.get(feed_dict["feed"]["entry"][i]["id"])
            VPWW53_soup = str(BeautifulSoup(VPWW53_xml.content, "xml"))
            #取得したXMLを、辞書型に変換する
            VPWW53_dict = xmltodict.parse(VPWW53_soup)
            
            break
```

これで、`金沢地方気象台`の最新の`VPWW53`が取得できました。
取得したデータは、下記のような形式となります。

```javascript:VPWW53_dictの中身
{
    'jmx:Report': {
        '@xmlns': 'http://xml.kishou.go.jp/jmaxml1/',
        '@xmlns:jmx': 'http://xml.kishou.go.jp/jmaxml1/',
        '@xmlns:jmx_add': 'http://xml.kishou.go.jp/jmaxml1/addition1/',
        'jmx:Control': {
            'jmx:Title': '気象特別警報・警報・注意報',
            'jmx:DateTime': '2023-12-20T14:28:20Z',
            'jmx:Status': '通常',
            'jmx:EditorialOffice': '金沢地方気象台',
            'jmx:PublishingOffice': '金沢地方気象台'
        },
        'Head': {
            '@xmlns': 'http://xml.kishou.go.jp/jmaxml1/informationBasis1/',
            'Title': '石川県気象警報・注意報',
            'ReportDateTime': '2023-12-20T23:28:00+09:00',
            'TargetDateTime': '2023-12-20T23:28:00+09:00',
            'EventID': None,
            'InfoType': '発表',
            'Serial': None,
            'InfoKind': '気象警報・注意報',
            'InfoKindVersion': '1.1_2',
            'Headline': {
            
                            ...中略...
            
            }
        },
        'Body': {
            '@xmlns': 'http://xml.kishou.go.jp/jmaxml1/body/meteorology1/',
            '@xmlns:jmx_eb': 'http://xml.kishou.go.jp/jmaxml1/elementBasis1/',
            'Notice': '石川県能登地方の地震で揺れの大きかった珠洲市、能登町では、地震による地盤の緩みを考慮し、大雨警報・注意報の土壌雨量指数基準を通常より引き下げた暫定基準で運用しています。',
            'Warning': [
                {
                
                        ...中略...
                
                },
                {
                
                        ...中略...
                
                },
                {
                
                        ...中略...
                
                },
                {
                    '@type': '気象警報・注意報（市町村等）',
                    'Item': [
                        {
                            'Kind': [
                                {
                                    'Name': '波浪警報',
                                    'Code': '07',
                                    'Status': '継続',
                                    'Property': {
                                        'Type': '波',
                                        'WarningPeriod': {
                                            'StartTime': {
                                                'Date': '２１日',
                                                'Term': '明け方'
                                            },
                                            'EndTime': {
                                                'Date': '２１日',
                                                'Term': '夜遅く'
                                            }
                                        },
                                        'AdvisoryPeriod': {
                                            'EndTime': {
                                                'Date': '２１日',
                                                'Term': '夜遅く'
                                            },
                                            'OverTime': 'にかけて\u3000以後も続く'
                                        },
                                        'WaveHeightPart': {
                                            'Base': {
                                                'jmx_eb:WaveHeight': {
                                                    '@description': '５メートル',
                                                    '@type': '波高',
                                                    '@unit': 'm',
                                                    '#text': '5'
                                                }
                                            }
                                        }
                                    }
                                },
                                {
                                    'Name': '雷注意報',
                                    'Code': '14',
                                    'Status': '継続',
                                    'Addition': {
                                        'Note': [
                                            '竜巻',
                                            'ひょう'
                                        ]
                                    },
                                    'Property': {
                                        'Type': '雷',
                                        'AdvisoryPeriod': {
                                            'EndTime': {
                                                'Date': '２１日',
                                                'Term': '夜遅く'
                                            }
                                        }
                                    }
                                },
                                {
                                    'Name': '強風注意報',
                                    'Code': '15',
                                    'Status': '継続',
                                    'Property': {
                                        'Type': '風',
                                        'AdvisoryPeriod': {
                                            'EndTime': {
                                                'Date': '２１日',
                                                'Term': '夜遅く'
                                            }
                                        },
                                        'PeakTime': {
                                            'Date': '２１日',
                                            'Term': '明け方'
                                        },
                                        'WindDirectionPart': {
                                            'Base': {
                                                'jmx_eb:WindDirection': {
                                                    '@description': '南西の風',
                                                    '@type': '風向',
                                                    '@unit': '８方位漢字',
                                                    '#text': '南西'
                                                }
                                            }
                                        },
                                        'WindSpeedPart': {
                                            'Base': {
                                                'Local': [
                                                    {
                                                        'AreaName': '陸上',
                                                        'jmx_eb:WindSpeed': {
                                                            '@description': '１６メートル',
                                                            '@type': '最大風速',
                                                            '@unit': 'm/s',
                                                            '#text': '16'
                                                        }
                                                    },
                                                    {
                                                        'AreaName': '海上',
                                                        'jmx_eb:WindSpeed': {
                                                            '@description': '２０メートル',
                                                            '@type': '最大風速',
                                                            '@unit': 'm/s',
                                                            '#text': '20'
                                                        }
                                                    }
                                                ]
                                            }
                                        }
                                    }
                                }
                            ],
                            'Area': {
                                'Name': '金沢市',
                                'Code': '1720100'
                            },
                            'ChangeStatus': '変化無'
                        },
                        {
                        
                            ...中略...
                        
                        },


                        ...中略...
                        
                    ]
                }
            ]
        }
    }
}
```

## 気象警報・注意報を取り出す
`金沢地方気象台`の`VPWW53`が取得できたので、`金沢市`の気象警報・注意報を取り出していきます。

まずは、先ほどの`VPWW53_dict`の中から`金沢市`の気象警報・注意報が書かれているところを探していきます。
そうすると、

`jmx:Report` → `Body` → `Warning` → 4個め →  `Item` → n個目 → `Kind`

に気象警報・注意報があることが分かります。

このことを踏まえると、下記のように記述することになります。先述のコードの`if  kisyodai == feed_dict["feed"]["entry"][i]["author"]["name"]:`の中の`break`の上に、下記コードをつけ足してください。

```py
            #市町村数を格納
            VPWW53_city_len = len(VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"])

            #市町村の数だけ繰り返し
            for j in range(VPWW53_city_len):

                #'金沢市'が見つかったら
                if city == VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Area"]["Name"]:
                    #金沢市の気象警報・注意報のデータを格納
                    kind = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"]


                    #気象警報・注意報が発表されていないとき
                    if type(kind) == dict and len(kind) == 1:
                        print('発表警報・注意報はなし')


                    #気象警報・注意報が1つのとき
                    elif type(kind) == dict:
                        status = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"]["Status"]

                        #気象警報・注意報が解除されていないとき
                        if status != '解除':
                            #気象警報・注意報名
                            warning = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"]["Name"]
                            print(warning)

                        #気象警報・注意報が解除されているとき
                        else:
                            print('発表警報・注意報はなし')


                    #気象警報・注意報が2つ以上のとき
                    elif type(kind) == list:
                        warnings = []

                        #気象警報・注意報の数だけ繰り返し
                        for w in range(len(kind)):
                            status = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"][w]["Status"]

                            #気象警報・注意報が解除されていないとき
                            if status != '解除':
                                #気象警報・注意報名
                                warning = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"][w]["Name"]
                                #気象警報・注意報名をリストに追加
                                warnings.append(warning)

                        #気象警報・注意報が1つ以上発表されているとき
                        if len(warnings) != 0:
                            for l in range(len(warnings)):
                                print(warnings[l])

                        #気象警報・注意報が発表されていないとき
                        else:
                            print('発表警報・注意報はなし')

                    break
```

:::note info
【参考程度に】Kindの出現の仕方は、下記の通りだと思います。(推測)
```py
#1. 気象警報・注意報が発表されていないときのKindの構造 (dict型)
     'Kind': {
         'Status': '発表警報・注意報はなし'
     }

     
#2. 気象警報・注意報が1つのときのKindの構造 (dict型)
     'Kind': {
         'Name': '強風注意報',
         # ...中略... #
     }

     
#3. 気象警報・注意報が複数のときのKindの構造 (list型)
     'Kind': [
           {
                 'Name': '波浪警報',
                 # ...中略... #
           },
           {
                 'Name': '雷注意報'
                 # ...中略... #
           },
           {
                 'Name': '強風注意報',
                # ...中略... #
           },
           
            # ...中略... #
     ]
```
:::

出力結果(例)は下記の通りです。

```:出力結果(例)
波浪警報
雷注意報
強風注意報
```

これで、`石川県金沢市`で発表されている気象警報・注意報を取得することができました。

# さいごに
最後まで見ていただきありがとうございました。
記事を書くことに慣れていないものでして、書き方や日本語がおかしいところがあるかもしれませんが、大目に見ていただけると幸いです。
この記事を書くにあたっていろいろ考えながら書いていたら、思いのほか時間がかかってしまったのは今回の反省です。
ですが、今年も防災アプリAdvent Calendarに参加できたことは嬉しく思います。
それでは、またの機会にお会いしましょう。以上、りーちゃんでした。

# 全体コード
<details><summary></summary>

```py
#パッケージをインポートする
import requests
from bs4 import BeautifulSoup
import xmltodict

## 大元のXMLのURLを指定
feed_url = 'https://www.data.jma.go.jp/developer/xml/feed/extra_l.xml'
#リクエスト(データを取得する)
feed_xml = requests.get(feed_url)
feed_soup = str(BeautifulSoup(feed_xml.content, "xml"))
#取得したXMLを、辞書型に変換する
feed_dict = xmltodict.parse(feed_soup)


#気象台と市町村名を指定
kisyodai = '金沢地方気象台'
city = '金沢市'

#XMLの中の'entry'の数だけ繰り返す
for i in range(len(feed_dict["feed"]["entry"])):

    #'VPWW53'が含まれるURLが見つかったら
    if 'VPWW53' in feed_dict["feed"]["entry"][i]["id"]:

        #'金沢地方気象台'が見つかったら
        if  kisyodai == feed_dict["feed"]["entry"][i]["author"]["name"]:
            #リクエスト(データを取得する)
            VPWW53_xml = requests.get(feed_dict["feed"]["entry"][i]["id"])
            VPWW53_soup = str(BeautifulSoup(VPWW53_xml.content, "xml"))
            #取得したXMLを、辞書型に変換する
            VPWW53_dict = xmltodict.parse(VPWW53_soup)


            #市町村数を格納
            VPWW53_city_len = len(VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"])

            #市町村の数だけ繰り返し
            for j in range(VPWW53_city_len):

                #'金沢市'が見つかったら
                if city == VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Area"]["Name"]:
                    #金沢市の気象警報・注意報のデータを格納
                    kind = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"]


                    #気象警報・注意報が発表されていないとき
                    if type(kind) == dict and len(kind) == 1:
                        print('発表警報・注意報はなし')


                    #気象警報・注意報が1つのとき
                    elif type(kind) == dict:
                        status = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"]["Status"]

                        #気象警報・注意報が解除されていないとき
                        if status != '解除':
                            #気象警報・注意報名
                            warning = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"]["Name"]
                            print(warning)

                        #気象警報・注意報が解除されているとき
                        else:
                            print('発表警報・注意報はなし')


                    #気象警報・注意報が2つ以上のとき
                    elif type(kind) == list:
                        warnings = []

                        #気象警報・注意報の数だけ繰り返し
                        for w in range(len(kind)):
                            status = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"][w]["Status"]

                            #気象警報・注意報が解除されていないとき
                            if status != '解除':
                                #気象警報・注意報名
                                warning = VPWW53_dict["jmx:Report"]["Body"]["Warning"][3]["Item"][j]["Kind"][w]["Name"]
                                #気象警報・注意報名をリストに追加
                                warnings.append(warning)

                        #気象警報・注意報が1つ以上発表されているとき
                        if len(warnings) != 0:
                            for l in range(len(warnings)):
                                print(warnings[l])

                        #気象警報・注意報が発表されていないとき
                        else:
                            print('発表警報・注意報はなし')

                    break
            break
```
</details>