ASTERIA Warpで地震情報をSlackに通知する



**(2021.2.28追記)
「[ASTERIA Warpで地震情報をSlackに通知する：気象庁APIバージョン](https://qiita.com/zrock/items/94280a189b3ae176832b)」に新しい気象庁サイトに対応した手順を書きました。
[HtmlParseコンポーネント][HtmlParse]の使い方が知りたい方はこのままこちらの記事を、新しい気象庁サイトからの情報の取得に興味のある方は「[ASTERIA Warpで地震情報をSlackに通知する：気象庁APIバージョン](https://qiita.com/zrock/items/94280a189b3ae176832b)」をお読みください。**

**(2021.2.25追記)
なんと記事公開直後、昨日(2021.2.24)の気象庁サイトのリニューアルにより、この記事のやり方では地震情報が取得できなくなっています。ASTERIA Warpの使い方の参考にはなると思いますので、記事は残しておきます。**

# 経緯

先日発生した地震で、社内で安否確認するために自動的にSlackに通知されるといいよねー的な話があり、とりあえず実現可能そうか試してみた。

要件的には、「地震が起きたらSlackに投稿して、安否確認のレスポンスを促す」ということ。もちろん、本当に被害が大きいときにSlackが大丈夫かとか、メールの方がキューイングされるので安心ではないか等々議論の余地はあるが、ひとまず、「地震が起きたらなにかする」を自動化できるかが自分的には興味があったのでやってみたわけです。

# プラットフォームの選択
コードで書けばできるのは分かってるわけですが、社内なので、まずはノーコードのプラットフォームとして **[ASTERIA Warp][Warp]** か **[Gravio](https://www.gravio.com/jp)** が候補に上がる。
Gravioだと基本的にエッジに置くことになり、これまた災害時のものとしてはイマイチなので却下。なので、今回は クラウドにも置けて比較的稼働が保証できるであろう **[ASTERIA Warp][Warp]** でやってみた。

# 地震情報をどうやって取得するか
地震情報といえば **緊急地震速報**。なわけですが、安否確認を促す前提であれば、即時性よりも正確さが肝になる。誤爆も多いこと、通知を受けること自体が簡単でないことを考えると却下。
気象庁のサイトはどうか？
「[地震情報（震源・震度に関する情報）][JMAEarthquake]」というページにいい感じのデータがまとまっている。震度やマグニチュード、場所も含めて、だいたい3～5分で情報が発表されているようだ。これくらいのタイムラグなら問題なし。時刻が全角だったり色々難はありますが、Warpを使えばなんとかなりそう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/d423ad30-4b08-76c4-c072-6b9d443aead9.png)
機械的にやるなら「[気象庁防災情報XMLフォーマット形式電文の公開（PULL型）](http://xml.kishou.go.jp/xmlpull.html)」というのもあるようなのだが、こちらもいまいち使い勝手がよくなさそう。Webhookとかそういうのあればいいのになぁ。。。平井大臣、頼みます。

# 使用環境

というわけで、使った環境は以下の通り。Warpの最新版でございます。

* ASTERIA Warp 2012.0991
 
# フローを作成する
概要はこんな感じ。

* 定期的に気象庁のサイトをスクレイピングして情報を取得する
* ある震度以上のときのみSlackに投稿する
* Slack投稿には、時刻、場所、マグニチュード、最大震度、詳細ページへのURLの情報を含む
* 二重投稿を防ぐために最後に投稿した地震の発生時刻を管理する

フローの全体感はこんな感じになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/3d9af90b-8d91-de3e-3ce9-05df92a70f55.png)


以下、ポイントを説明していきます。

## HTMLを取得して表の情報を取得する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/4efcee4e-6f03-dfb6-9d9a-4b6ce66baec9.png)

まずHTTPGetで気象庁の該当ページを取得します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/f459fd83-2915-9dcf-4cda-c460de7dbdf0.png)

次に取得したデータからHtmlParseを使用してレコードストリームに展開します。ここが今回の肝にもなります。

まずはソースコードを見て、 表のある部分を探します。ありました。ありがたいことに、`id`が指定されてるので簡単です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/5474a014-1654-7ef1-a580-032e840c0477.png)

これを使用してベースセレクターを指定します。ベースとなるのは `<tr>`タグですが、最初の行は子が`<th>`となりヘッダになるので、それを除外するために `.gt(0)`と指定して **2行目以降の行**を指定します。
ベースURLはページのURLの上位を指定しておきます。これは、取得する詳細ページへのURLが相対パスになっているためです。
ループも開始しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/e22c5ce8-c2a6-3ee5-1a09-6895d948fab0.png)


各データは `<table>` タグの中で管理されてます。こちらも各 `<td>` タグにクラスでも指定してあれば簡単だったのですがそんなのは指定されていない。普通のテーブルなので、仕方なくインデックスで指定します。
最後に詳細へのURLもほしいので、`<a>`タグの`href`の値を取得しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/c8d709f5-3abf-36a0-5186-f5153a58d128.png)

HtmlParseコンポーネントの記法等についての詳細は、[こちらのヘルプ][HtmlParse]を見てみてください。

## Mapperで正規化
表記が微妙なので、いろいろ正規化します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/3f7f4866-58d0-c33a-a465-eeb319ce7876.png)


まずはMultiFieldMapperを使って、一括で全角から半角へ変換。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/f184802f-4703-d960-ed09-47081f7814b8.png)

半角に揃えたら、次のMapperで各データの正規化をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/a6a2ad7a-5882-328f-4119-c2b575f81ded.png)

時刻データはStrToDateStd関数を使ってDateTime型に変換します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/41ede290-7990-5a5c-68d5-2659f5b334bb.png)

マグニチュードはTruncate関数で先頭`M`を取り除きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/222610fc-bbc2-9bbd-4341-3a7a18b99aa5.png)

震度はRegexp関数を使って正規表現で数字だけを抽出します。震度5以上の「強」「弱」は、どちらも十分大きな地震で安否確認のためのフィルター条件としてはあまり意味がないので無視しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/a4d3c38c-ad3a-49d3-61e3-225fbc54a321.png)



また、前回から複数の地震が見つかった場合のことを考えて、時刻で昇順にソートしておきます。（元データは最新が上に来る降順）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/149791e3-83b7-c295-eb32-4b86f9fa6143.png)

## 投稿条件を指定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/71e08307-3de9-3b57-4803-6f1029921532.png)

今回は安否確認が必要なので最大震度4以上としておきます。最大震度が4以上ならSlack投稿へ、そうでなければ無視します。
また、多重投稿を防ぐため、最後に投稿した地震発生日時よりも新しいときだけになるように条件を追加しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/090a1534-890c-ac4c-1620-63986469d0ac.png)


条件式はこんな感じですね。

```:BranchStart条件式
record.SeismicTime > $exvar.Earthquake.LastProcessed && record.SeismicIntensity >= 4
```

最終投稿の地震発生日時を保存するには、外部変数のアプリケーション変数を利用します。こうすることで、Warpを終了したりしても情報が残っていて便利です。条件式で参照するには `$exvar.`のプレフィックスで参照できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/de2f4a34-2120-c393-249f-def4a43a97da.png)


## Slackに投稿

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/b96945d3-5574-5c47-f1f5-eecf8dbe0835.png)

メッセージを作成します。要件の通り、時刻、場所、マグニチュード、最大震度、詳細ページへのURLを含めます。Slackはメッセージ内のURLからリンクを作ってくれるのでそのまま追加しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/e970989a-5e27-1324-621b-97761bdfba49.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/0f8dec05-2e1e-ddfa-463d-5361d48bec5e.png)


## 最終投稿日を保存する
投稿したら投稿した地震の発生日時をアプリケーション変数に保存しておきます。これはメッセージ作成のマッパーで同時に実行しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/c9ffb784-7b12-4de7-00cb-f8f90d79155a.png)

# 実行
震度4以上の地震が拾われてSlackに投稿されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/c5c9e400-66dc-8c2b-bce5-72c864bd3a73.png)

# スケジュールトリガーを設定する
気象庁のページは分単位で時刻管理されているようなので、1分毎にフローが実行されるようにスケジュールトリガーを作成しておきます。これより間隔を延ばすと即時性が薄くなりますし、短くしてもあまり意味がないのでこれくらいで。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/e44f67bd-5faa-54cd-7f39-6f373419b933.png)

# 結果
震度4以上だとなかなか発生しないので、震度3に設定して放置しておいたら、見事投稿されてました。16:09発生が16:13に投稿されているので、安否確認を促すという意味ではまずまずなんじゃないでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/19bc9248-6acb-bc39-6ee1-f768aa74827b.png)

# 今後の課題
とりあえず大きな地震が発生したらSlackに投稿することはできました。
あとは、メッセージをうまく調整すれば簡易な安否確認プラットフォームができますね。
課題としてはいかのようなことがまだ残っていますので、また考えていければなぁと思ってます。

* 安否確認をどうするか。リアクション？ボット？
* 全国の地震を全部拾うのか？震央は文字列ですが表示されてるので、なんとか位置を拾うことはできるかもしれないが、出張等で社員がどこにいるかは特定できないので日本全国拾っておくべきなのか。事業所があるところだけにしたほうがいいのか？


# 参考

* [気象庁の地震情報（震源・震度に関する情報）][JMAEarthquake]
* [ASTERIA Warp][Warp]
* [HtmlParseコンポーネント][HtmlParse]


[JMAEarthquake]:https://www.jma.go.jp/jp/quake/quake_singendo_index.html
[Warp]:https://www.asteria.com/jp/warp/
[HtmlParse]:https://help.asteria.com/documentation/warp/ja/2012/flow/designer/reference/component/HtmlParse.html