NHKの地震情報を掻っ攫う方法

> 2019-04-05 追記 : NHKの災害情報ページがリニューアルしていましたが、APIの変更は無いみたいなのでリンクだけ修正しました。

この方法はNHKが公式に発表しているものでもなければ、この記事に書かれていることを行ったことによる損害を筆者が負うものでもありません！
NHKがお家を訪問してきたり、NHKニュースを読むことができなくなるかもしれません！


## NHKの地震情報(震度マップ)を取得したい
地震駆動型プログラマの筆者は、5月25日21時13分ごろに発生した長野県で震度５強を観測する地震を受け、NHKの地震情報をどうにかして手に入れられないか？　と悩んでいました。

情報元 : [NHK あなたの天気・防災｜地震情報](https://www.nhk.or.jp/kishou-saigai/earthquake/)

最初は任意の言語でWebスクレイピングすればいいかなとか考えていましたが、開発者ツールで覗いてみると「どうぞ再利用してください」と言わんばかりの非常に簡単な実装がされていました。

### スクレイピングを試す(試してない)
手始めに情報元のHTMLソースを覗いてみると、すでにデータが入力されているわけでなく、後からデータを引っ張ってくる非同期式の方法をとっていました。

この場合、スクレイピングの実装が複雑になってしまいます。
しかし、非同期式のためどこかのJavaScriptでデータを読みに行っている可能性が非常に高いのではないかと勘付きました。


### 開発者ツールのネットワークタブを確認する
最初、私は「震度マップ」のみを取ることができれば良い、と考えていましたが、まとめてデータが取れることに後々気づきました。

ウォーターファールを見ているとそれっぽいファイルが……
![image.png](https://qiita-image-store.s3.amazonaws.com/0/157845/e49e2bcb-dc8b-ff53-e54a-e3801ca8309e.png)

試しに、`JishinReport.xml`を覗いてみると以下のXMLソースが取れました。

#### JishinReport.xml

```xml:JishinReport.xml

<jishinReport>
    <record date="2018年05月25日">
        <item time="21時28分ごろ" shindo="2" url="https://www3.nhk.or.jp/sokuho/jishin/data/JSA0180525212844_20180525213138.xml">長野県北部</item>
    </record>
    <record date="2018年05月23日">
        <item time="14時04分ごろ" shindo="1" url="https://www3.nhk.or.jp/sokuho/jishin/data/JSA0180523140446_20180523140848.xml">父島近海</item>
    </record>
    ︙
    ︙
    ︙
    <record date="2018年05月19日">
        <item time="17時35分ごろ" shindo="1" url="https://www3.nhk.or.jp/sokuho/jishin/data/JSA0180519173518_20180519173825.xml">北海道北西沖</item>
        <item time="08時50分ごろ" shindo="1" url="https://www3.nhk.or.jp/sokuho/jishin/data/JSA0180519085005_20180519085308.xml">山梨県東部・富士五湖</item>
    </record>
</jishinReport>

```

また、パラメータとして`t`があり、UNIXタイムスタンプかな？と変換ツールにかけてみたのですが現時刻とは明らかにかけ離れている日時を示したため、何か別の時刻表記を使用しているのかもしれません。
これは要研究。

なお、パラメータ`t`を付与しないことによるエラーは発生しない模様。
むしろ、最新の情報を返してくれる。

### JishinReport.xmlの構造
| 要素名 | 説明 | 親要素 | 含まれる数 | 属性 |
|--:|:--|:--|:--|:--|
|`jishinReport`|ルート要素|-|1|-|
|`record`|日付|`jishinReport`|変動|`data` : 日付(`Y年m月d日`)|
|`item`|各地震|`record`|変動|`time` : 時間(`H時i分ごろ`)<br />`shindo` : 最大震度<br />`url` : 地震の詳細情報XML URL|

すごくシンプル！！！
しかも、`item`の属性`url`から地震情報が取れる！　つよい！

### JishinReport.xmlのitem[url]から詳細情報を取得する

#### 詳細情報XML

```xml:JSA0180525220014_20180525220427.xml
<Root>
    <Timestamp>2018/05/25 22:04:27</Timestamp>
    <Earthquake Id="A0180525220014" Time="2018/05/25 22:01:00" Intensity="1" Epicenter="長野県北部" Latitude="北緯 36.9度" Longitude="東経 138.6度" Magnitude="2.2" Depth="10km">
        <Detail>data/JS00cwA0180525220014_20180525220427.jpg</Detail>
        <Local>data/JS00cuA0180525220014_20180525220427.jpg</Local>
        <Global>data/JS00clA0180525220014_20180525220427.jpg</Global>
        <Relative>
            <Group Intensity="1">
                <Area Name="栄村"/>
            </Group>
        </Relative>
    </Earthquake>
</Root>
```

### 詳細情報XMLの構造
| 要素名 | 説明 | 親要素 | 含まれる数 | 属性 |
|--:|:--|:--|:--|:--|
|`Root`|ルート要素|-|1|-|
|`Timestamp`|情報**発表**日時(`Y/m/d H:i:s`)|`Root`|1|-|
|`Earthquake`|地震データ|`Root`|1|`Id` : 地震ID?<br />`Time` : 地震**発生**日時(`Y/m/d H:i:s`)<br />`Intensity` : 最大震度<br />`Epicenter` : 震源地<br />`Latitude` : 緯度(`北緯 xx.x度`)<br />`Longitude` : 経度(`東経 xxx.x度`)<br />`Magnitude` : マグニチュード<br />`Depth` : 震源の深さ|
|`Detail`|震度マップ(概況)|`Earthquake`|1||
|`Local`|震度マップ(拡大)|`Earthquake`|1||
|`Global`|震度マップ(広域)|`Earthquake`|1||
|`Relative`|各地の震度|`Earthquake`|1||
|`Group`|同じ震度の集合|`Relative`|変動|`Intensity` : 震度(○弱は`○-`,○強は`○+`)|
|`Area`|地名(要素のみ)|`Group`|変動|`Name` : 地名|

すごい！！　直感的！！
もっと複雑なものかと思ったら、意外にもシンプルでした。

## 最新の震度マップ及び諸々の情報をまとめるページを作る

これこそ、情報元でも十分なのですが再利用したいという気持ちは変わらないので、PHPを用い画像と情報を引っ張って表示するサンプルプログラムを書きます。

### サンプルコード

```php:nhk.php
<?php
    /* NHKの地震情報をゴニョゴニョして貰う方法 by nirot1r */

    $imageBaseURL = "http://www3.nhk.or.jp/sokuho/jishin/"; /* 1 */

    $rawReportXML = mb_convert_encoding(file_get_contents("http://www3.nhk.or.jp/sokuho/jishin/data/JishinReport.xml"), "UTF-8", "SJIS"); /* 2 */

    /* 3 */
        $dump = explode("\n", $rawReportXML, 2);
        $rawReportXML = '<?xml version="1.0" encoding="UTF-8" ?>' . $dump[1];
        $xmlData = new SimpleXMLElement($rawReportXML);

    $latestItemURL = $xmlData->record[0]->item[0]["url"]; /* 4 */

    $rawLatestEarthquake = mb_convert_encoding(file_get_contents($latestItemURL), "UTF-8", "SJIS"); /* 5 */;

    /* 3 */
        $dump = explode("\n", $rawLatestEarthquake, 2);
        $rawLatestEarthquake = '<?xml version="1.0" encoding="UTF-8" ?>' . $dump[1];
        $earthquakeXMLData = new SimpleXMLElement($rawLatestEarthquake);

    $earthquake = $earthquakeXMLData->Earthquake; /* 6 */
?>
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1;">
        <title>NHKの地震情報をゴニョゴニョして貰う方法 by nirot1r</title>
    </head>
    <body>
        
        <h1><?= $earthquake["Time"] ?>に発生した地震</h1>
        <p>
            震源 : <?= $earthquake["Epicenter"] ?><br />
            震源の深さ : <?= $earthquake["Depth"] ?><br />
            マグニチュード : <?= $earthquake["Magnitude"] ?><br />
            最大震度 : <?= $earthquake["Intensity"] ?>
        </p>

        <h2>震度マップ(概況)</h2>
        <img src="<?= $imageBaseURL . $earthquake->Detail ?>" />

        <h2>震度マップ(拡大)</h2>
        <img src="<?= $imageBaseURL . $earthquake->Local ?>" />
        
        <h2>震度マップ(広域)</h2>
        <img src="<?= $imageBaseURL . $earthquake->Global ?>" />

    </body>
</html>
```

~~デモ(中身のコードはほとんど同じ)~~
> 2019-10-03追記 : サーバを移転した影響で、デモもどこかへ旅立ちました。
気が向いたらNuxt.jsで書き直します。

キャッシュなどの処理は実装していませんので、大量のアクセスは控えるように！

### コード解説
#### 1 : 画像ベースURL代入
震度マップのベースURLを代入します。

#### 2 : 地震情報リスト取得
NHKから地震情報リストを取得します。
この時に`Shift-JIS`から`UTF-8`にエンコード変換しておきます。

#### 3 : エンコード変更
2で受信したXMLは`Shift-JIS`のためSimpleXMLで処理することができません。
そこで、XMLのエンコード宣言部分を書き換え、SimpleXMLで扱えるようにします。

#### 4 : 詳細情報XML URL取得
各オブジェクトの先頭(`0`)にある要素のURLを取得します。

#### 5 : 詳細情報取得
4で取得したURLを基に、NHKにリクエストを行います。
2と同様、エンコード変換を行います。

#### 6 : 地震オブジェクトを作成
地震情報へアクセスしやすいように、地震オブジェクトを作成します。

#### おわり
あとは、`$earthquake`から必要な情報を取りだして終了です。

### 実行結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/157845/a9e33344-9382-2853-8901-606fd8078354.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/157845/8167c563-183f-cbc9-668c-2a5f92e3928f.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/157845/5063e36c-93f0-088a-1853-77a980cc147e.png)

## まとめ
本当に開発者ファーストかと思うぐらい簡単に取得できました。
ただ、`Shift-JIS`であることだけが注意点です。それ以外は非常によくまとまっていたと思います。

最後にも書きますが、試す際は自己責任でお願いします。
その他、「こうしたほうがいいよー！」「誤字あるじゃん」などありましたらコメント・編集リクエストでどうぞ！