天気予報を取得するシェルスクリプトを組んでみた

シェルを使用して遊んでみたくなったので、天気予報を取得するシェルスクリプトを組んでみました。コンセプトとして、都道府県をコマンドラインから入力してもらい、入力された都道府県に合致する天気予報を表示するというもの。尚今回は東京・名古屋・大阪のみに対応しており、それ以外は対応不可というようなメッセージを表示するようにしています。(上記の県以外に住んでいる方、誠に申し訳ありません...)
実際の完成したファイルがこちら。
```get_weather.sh
echo '天気予報を確認したい都道府県を入力してね'
read PREFECTURE
case "$PREFECTURE" in
'東京') curl https://tenki.jp/forecast/3/16/4410/13104/ 2> /dev/null | grep -e "weather-telop" | sed -e 's/<[^>]*>//g' -e 's/ //g' -e "s/^/今日の東京の天気は/" | head -n1;;
'名古屋') curl https://tenki.jp/forecast/5/26/5110/23100/ 2> /dev/null | grep -e "weather-telop" | sed -e 's/<[^>]*>//g' -e 's/ //g' -e "s/^/今日の名古屋の天気は/" | head -n1;;
'大阪') curl https://tenki.jp/forecast/6/30/6200/27100/ 2> /dev/null | grep -e "weather-telop" | sed -e 's/<[^>]*>//g' -e 's/ //g' -e "s/^/今日の大阪の天気は/" | head -n1;;
*) echo '現在対応できるのは東京・名古屋・大阪だけです...';;
esac
```

それではコマンドについて順番に解説していきたいと思います。
まずはコマンドラインから入力された値を受け取るようにする為、readコマンドを使用して入力された値をPREFECTUREという変数に格納しています。上記で代入された変数は$を変数名の前につけることでアクセスできるようになります。

```get_weather.sh
read PREFECTURE
```

次に入力された値によって処理を分けたいので、case分を使用しています。東京・名古屋・大阪以外は今回は対応しないので、それ以外の場合は*)の部分で拾うようにして、メッセージを表示しています。

```get_weather.sh
case "$PREFECTURE" in
~~~;;
~~~;;
~~~;;
*) echo '現在対応できるのは東京・名古屋・大阪だけです...';;
esac
```

curlコマンドを使用して、指定のurlを叩きそのページのhtmlを取得します。今回はtenki.jpという天気予報を掲載しているwebページを参考するようにしました。tenki.jpを選んだのにも理由があり、curlコマンドで叩いた場合javascript実行前のhtmlが返却されます。tenki.jp以外の天気予報サイトではjavascript実行後に天気情報をhtmlに展開しているようなケースが多くあり、欲しい状態のhtmlが取得できませんでした。tenki.jpではそのような動作はしていなかった為、今回選ぶに至りました。もしjavascript実行後のhtmlが欲しい場合は、Seleniumなどを使用する手もありますが、今回はスルーさせて頂きます。それぞれのurlが異なりますが、url先によって各都道府県の天気情報が記載されており、今回はその分岐をしたかったが為にcase文を使用した訳です。

```get_weather.sh
'東京') curl https://tenki.jp/forecast/3/16/4410/13104/
'名古屋') curl https://tenki.jp/forecast/5/26/5110/23100/
'大阪') curl https://tenki.jp/forecast/6/30/6200/27100/ 
```

curlコマンドによって必要なhtmlは取得できましたので、ここから必要な情報のみを取得できるようにしていきます。スクリプトに"weather-telop"という記載がありますが、これはhtmlの1つのクラスを表しており、このクラスの要素に晴れ・曇りなど必要な天気情報が記載されています。なので取得したhtmlからこちらの要素だけ出力するようにgrepコマンドで指定しています。grepコマンドは検索対象から指定した文字列を含む行を表示するコマンドで-eオプションを使用することで引数として検索文字列を指定することができます。2> /dev/nullはgrepを使用した場合、標準エラー出力も合わせて表示されてしまう為それを破棄する為に使用しています。

```get_weather.sh
2> /dev/null | grep -e "weather-telop"
```

現在の表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576652/687b87da-f0a2-34ae-ab14-74a7f170cbb2.png)
上記で対象の要素だけを出力できるようになりましたが、このままだとhtmlタグ要素も含めた内容が表示されてしまいます。それを回避する為、sedコマンドを使用しています。sedコマンドは文字列を置換する際などに使用されるコマンドで、s/置換前文字列/置換後文字列/のような形式で置換することが可能です。後ろに/gの表示がありますが、これは/gなしの場合複数マッチした際は先頭のみ置換を行いますが、/gオプションをつけることで対象全ての置換を行うことができます。<[^>]*>の表記はHTMLタグを表しており、これはそのまま覚えてしまいましょう。

```get_weather.sh
sed -e 's/<[^>]*>//g'
```

後は半角スペースが残った状態になっているので、続けて-eオプションを記載して半角スペースを消していきます。こちらも上記の説明と同じような考え方ですね。

```get_weather.sh
-e 's/ //g'
```

最終的に、今日の◯◯の天気は~という形で出力したいので、更に続けて-eオプションを記載して先頭に文字を追加するようにしています。^は先頭を表すので、先頭に今日の◯◯の天気はという文言を追加している訳ですね。

```get_weather.sh
-e "s/^/今日の◯◯の天気は/"
```

最後に"weather-telop"の要素はhtml内に、今日・明日分の2つあり、現状のままですと2つ出力されてしまいます。それを回避する為、headコマンドを使用します。headコマンドは標準出力の先頭のn行を抜き出すコマンドで、-nオプションを指定することで抜き出す行数を指定しています。

```get_weather.sh
head -n1
```

上記をもって、スクリプトが完成しました。実際に入力してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576652/edfcd6b2-2e25-bff6-f389-fab4c202cc5f.png)
無事に本日分の天気予報が表示されました。実用性はともかく、なんか嬉しいですね。

## 終わりに
html要素に依存している為、ページ内のクラスや構成が変わった場合途端に機能しなくなってしまうので、実用性にはかなり欠けますがシェルスクリプトを学ぶには良い機会になったと思います。もし次にやるとしたらSeleniumなどを使用して、SPA等でできているサイトからも天気情報を取得できるようにできたら良いですね〜