leopardgeckoのブログ

Macの関連事項など

GeekToolでお天気表示 改訂版

(以前GeekToolで天気を表示するためのスクリプトを公開したのですが、見直してみると地域やプロキシの変更が面倒そうなのでもっと簡単に設定を変えられるように全面的に改定してみました。前回の記事を読んでいない人にもわかるように記述がかぶりますがご容赦ください)

はじめに

GeekToolとはMacのデスクトップをカスタマイズできるソフトです。スクリプト次第で色々なものを表示できて便利なのですが、スクリプトが公開されているものでなければ自分でスクリプトを書かなければなりません。
天気を表示するスクリプトは情報が古くて今では使えないものが多い上に自力で作るとなるとHTMLソースを解析する必要があるなど結構大変な作業なので、AccuWeatherという天気予報サイトを利用したスクリプトをご紹介します。GeekTool自体の使い方は他の入門用サイトを参照してください。

例えば下の画像のような感じで表示させるとします。一番上の小さめの文字が本日の最高・最低気温。一番大きな文字が現在の気温・天気、その左が現在の天気の画像です。中段のずらっと並んだ画像と文字が時間単位の天気予報で、とりあえず16時間後まで表示しています。下段が明日と週末の天気予報です。

f:id:leopardgecko:20171012152230p:plain

<各スクリプトの説明>

現在の気温と天気

現在の気温と天気を表示してリアルな画像の天気アイコンを取得するスクリプトを下に示します。Propertiesウインドウの「Refresh every 〜s」のところで表示を更新する秒数を設定するのを忘れないでください。

このままでは東京都江東区の天気が表示されてしまうので、スクリプトを書き換える必要があります。まずhttps://www.accuweather.com/en/jp/japan-weatherにアクセスして、天気を表示したい市や区の名前をローマ字で入力して検索するとその地域の天気が表示されるようになりますので、そこのURLを記録してください。「https://www.accuweather.com/en/jp/場所/数字/weather-forecast/数字」のような形式になるはずです。下のスクリプトの「# 場所のURL」の下に「weather_url="https://www.accuweather.com/en/jp/koto-ku/221230/weather-forecast/221230"」という記述がありますが、この赤い文字で示した部分が表示したい場所のURLです。ここを上で記録したURLに書き換えてください。このURLは他のスクリプトでも使います。

(社内での使用などでプロキシを指定する必要がある場合は、「# プロキシ」の次の行の「weather_proxy=""」を「weather_proxy="--proxy http://hogehoge:8080"」のように適宜書き換えてください)

天気アイコンの画像は「/tmp/weather_now.png」に保存されるので、GeekToolの「Image」をデスクトップにドラッグ&ドロップしてURLを「file://localhost/private/tmp/weather_now.png」と指定してください。上のスクリプトと同じようにRefreshの秒数設定も忘れずに。

画像をリアルではなく他と同じシンプルなものにしたい場合は、スクリプトの最後の方の「https://vortex.accuweather.com/adc2010/images/icons-numbered/」のところを「https://vortex.accuweather.com/adc2010/images/slate/icons/」に変えてください。

# 現在の気温と天気のスクリプト

# 場所のURL
weather_url="https://www.accuweather.com/en/jp/koto-ku/221230/weather-forecast/221230"

# プロキシ
weather_proxy=""

# 元データ取得
weather_data=`curl $weather_proxy --silent $weather_url`

# 現在の温度と天気を取得して表示
echo "$weather_data" | grep -A 8 'Current Weather' | grep -e 'large-temp\|cond' | sed -e 's/<[^>]*>//g' | sed -e 's/&deg;/°/g' -e 's/^ *//' | tr "\r\n" " "

# 天気アイコンのナンバーを取得し二桁でゼロパディングする
icon_cur=`echo "$weather_data" | grep -A 2 'Current Weather' | grep 'icon' | sed -e 's/[^"]*"\([^"]*\)".*/\1/' | tr -cd '0123456789' | awk '{printf "%02d", $1}'`

# 天気アイコンナンバーをURLに変換して画像を保存
echo "https://vortex.accuweather.com/adc2010/images/icons-numbered/"$icon_cur"-xl.png" | xargs curl $weather_proxy --silent -o /tmp/weather_now.png

最高気温と最低気温

最高気温と最低気温の表示は以下の通りです。AccuWeatherでは夜になると最低気温のみの表示に変わるのですが、それにも対応させてあります。「場所のURL」は上と同じように天気を表示したい地域のURLに書き換えて、必要がある方はプロキシも書き換えてください。以下のスクリプトも全て同様です。

# 最高気温と最低気温のスクリプト

# 場所のURL
weather_url="https://www.accuweather.com/en/jp/koto-ku/221230/weather-forecast/221230"

# プロキシ
weather_proxy=""

# 元データ取得
weather_today=`curl $weather_proxy --silent ${weather_url/weather-forecast/daily-weather-forecast}"?day=1"`

# 今日の最高温度と最低温度を取得して表示
echo "$weather_today" | grep -A 6 'Today\|Tonight' | grep  -e 'large-temp\|small-temp\|temp-label tonight selected' | sed -e 's/<[^>]*>//g' | sed -e 's/&deg;/°/g' -e 's/^ *//' | tr "\r\n" " "

時間単位

以下は現在時刻から8時間後までと、9時間後から16時間後までのスクリプトです。これは等幅フォントを指定しないと表示がずれますのでご注意を。(上の画像ではRicty Discordというプログラミング用の等幅フォントを使っています)

天気アイコンの画像は「/tmp/weather_hour_0.png」から連番で「/tmp/weather_hour_7.png」まで保存されるので、上の応用で画像を一つずつ貼り付けて並べてください。

視認性を上げるためにamとpmの色を変えていますが、色を変えたくない場合は「時刻を左揃え8桁で表示」のところの「 | sed -e s/am/`echo "\033[0;31mam\033[0m"`/g -e s/pm/`echo "\033[0;34mpm\033[0m"`/g」をごそっと削除してください。(「printf "%-8s" ${current_time[*]}」だけ残す)

まず一つ目、現在時刻から8時間後までのスクリプトはこんな感じです。

# 現在時刻から8時間後までのスクリプト

# 場所のURL
weather_url="https://www.accuweather.com/en/jp/koto-ku/221230/weather-forecast/221230"

# プロキシ
weather_proxy=""

# 元データ取得
my_hour=`date +%H`
curl_data=`curl $weather_proxy --silent ${weather_url/weather-forecast/hourly-weather-forecast}"?hour=$my_hour"`

# 8時間分の時刻を取得(後で配列変数として使う。以下同様)
current_time=(`echo "$curl_data" | grep -A 37 "overview-hourly" | grep -A 29 "first-col" | sed -e 's/<[^>]*>//g' -e 's/^ *//' | tr -d "\r"`) 

# 8時間分の天気を取得(データにスペースが入ることがあるのでIFSを使って処理)
_IFS="$IFS";IFS="_"
current_weather=(`echo "$curl_data" | grep -A 67 "overview-hourly" | grep -A 23 "Forecast" | sed -e 's/<[^>]*>//g' -e 's/^ *//' -e '1,2d' -e 's/ \&amp;/\&/g'| tr -s '\r\n' '_'`)
IFS="$_IFS"

# 8時間分の天気アイコンのナンバーを取得
current_icon=(`echo "$curl_data" | grep -A 38 "overview-hourly" | grep 'icon' | sed -e 's/[^"]*"\([^"]*\)".*/\1/' | tr -cd '0123456789\n'`)

# 時刻を左揃え8桁で表示
printf "%-8s" ${current_time[*]} | sed -e s/am/`echo "\033[0;31mam\033[0m"`/g -e s/pm/`echo "\033[0;34mpm\033[0m"`/g

# 天気を左揃え8桁かつ2段で表示
for (( i=0; i < ${#current_weather[@]}; ++i))
do
echo "${current_weather[$i]}" | awk '{printf "%-8s", $1}'
done
echo
for (( i=0; i < ${#current_weather[@]}; ++i))
do
echo "${current_weather[$i]}" | awk '{printf "%-8s", $2}'
done

# 天気アイコンを取得して保存(URLに使うアイコンのナンバーはゼロパディングする)
for (( i=0; i < ${#current_icon[@]}; ++i))
do
current_icon[$i]=`printf "%02d" ${current_icon[$i]}`
echo "https://vortex.accuweather.com/adc2010/images/slate/icons/${current_icon[$i]}-s.png" | xargs curl $weather_proxy --silent -o /tmp/weather_hour_$i.png
done

9時間後から16時間後まではこんな感じ。上のスクリプトの応用ですから内容はほとんど同じです。「later=」の後の数値を「16」にすれば17時間後から24時間後の表示にすることもできます。天気アイコンの画像は「/tmp/weather_hour_8.png」から連番で「/tmp/weather_hour_15.png」まで保存されます。

# 9時間後から16時間後までのスクリプト

# 場所のURL
weather_url="https://www.accuweather.com/en/jp/koto-ku/221230/weather-forecast/221230"

# プロキシ
weather_proxy=""

# 何時間後?
later=8

# 元データ取得
my_hour=`date +%H` ; my_hour=`expr $my_hour + $later`
curl_data=`curl $weather_proxy --silent ${weather_url/weather-forecast/hourly-weather-forecast}"?hour=$my_hour"`

# 時刻を取得
current_time=(`echo "$curl_data" | grep -A 37 "overview-hourly" | grep -A 29 "first-col" | sed -e 's/<[^>]*>//g' -e 's/^ *//' | tr -d "\r"`)

# 天気を取得(スペースを含んだデータを配列に格納)
_IFS="$IFS";IFS="_"
current_weather=(`echo "$curl_data" | grep -A 67 "overview-hourly" | grep -A 23 "Forecast" | sed -e 's/<[^>]*>//g' -e 's/^ *//' -e '1,2d'  -e 's/ \&amp;/\&/g' | tr -s '\r\n' '_'`)
IFS="$_IFS"

# 天気アイコンのナンバーを取得
current_icon=(`echo "$curl_data" | grep -A 38 "overview-hourly" | grep 'icon' | sed -e 's/[^"]*"\([^"]*\)".*/\1/' | tr -cd '0123456789\n'`)

# 時刻を左揃え8桁で表示
printf "%-8s" ${current_time[*]} | sed -e s/am/`echo "\033[0;31mam\033[0m"`/g -e s/pm/`echo "\033[0;34mpm\033[0m"`/g
# 天気を左揃え8桁かつ2段で表示
for (( i=0; i < ${#current_weather[@]}; ++i))
do
echo "${current_weather[$i]}" | awk '{printf "%-8s", $1}'
done
echo
for (( i=0; i < ${#current_weather[@]}; ++i))
do
echo "${current_weather[$i]}" | awk '{printf "%-8s", $2}'
done

# 天気アイコンを取得して保存(取得するアイコンのナンバーはゼロパディングする)
for (( i=0; i < ${#current_icon[@]}; ++i))
do
current_icon[$i]=`printf "%02d" ${current_icon[$i]}`
echo "https://vortex.accuweather.com/adc2010/images/slate/icons/${current_icon[$i]}-s.png" | xargs curl $weather_proxy --silent -o /tmp/weather_hour_`expr $i + $later`.png
done

明日の天気

明日の天気は以下の通り。AccuWeatherでは深夜を過ぎるとその日の早朝の天気に表示が変わりますが、それにも対応させてあります。天気アイコンは「/tmp/weather_tomorrow.png」に保存されます。

# 明日の天気のスクリプト

# 場所のURL
weather_url="https://www.accuweather.com/en/jp/koto-ku/221230/weather-forecast/221230"

# プロキシ
weather_proxy=""

# 元データ取得
weather_data=`curl $weather_proxy --silent $weather_url`

# 明日(または早朝)の天気を表示
echo "$weather_data" | grep -e 'Tomorrow\|Early' | sed -e 's/<[^>]*>//g'  -e 's/^ *//' 
echo "$weather_data" | grep -A 9 'Tomorrow\|Early' | grep  -e 'large-temp\|cond' | sed -e 's/<[^>]*>//g' | sed -e 's/&deg;/°/g' -e 's/^ *//' | tr "\r\n" " "

# 明日の天気アイコンのナンバーを取得しゼロパディングする
icon_data=`echo "$weather_data" | grep -A 2 'Tomorrow\|Early' | grep 'icon' | sed -e 's/[^"]*"\([^"]*\)".*/\1/' | tr -cd '0123456789' | awk '{printf "%02d", $1}'`

echo "https://vortex.accuweather.com/adc2010/images/slate/icons/"$icon_data"-l.png" | xargs curl $weather_proxy --silent -o /tmp/weather_tomorrow.png

週末の天気

週末の天気は以下の通り。天気アイコンの場所は土曜日が「/tmp/weather_sat.png」、日曜日が「/tmp/weather_sun.png」です。

# 土日の天気のスクリプト

# 場所のURL
weather_url="https://www.accuweather.com/en/jp/koto-ku/221230/weather-forecast/221230"

# プロキシ
weather_proxy=""

# 元データ取得
weather_weekend=`curl $weather_proxy --silent ${weather_url/weather-forecast/weekend-weather}`

# 土日の天気を表示
echo "\033[0;34mSaturday\033[0m"
echo "$weather_weekend" | grep -A 9 'Saturday' | grep  -e 'large-temp\|cond' | sed -e 's/<[^>]*>//g' | sed -e 's/&deg;/°/g' -e 's/^ *//' | tr "\r\n" " "
echo "\n\033[0;31mSunday\033[0m"
echo "$weather_weekend" | grep -A 9 'Sunday' | grep  -e 'large-temp\|cond' | sed -e 's/<[^>]*>//g' | sed -e 's/&deg;/°/g' -e 's/^ *//' | tr "\r\n" " "

# 土日の天気アイコン取得
icon_sat=`echo "$weather_weekend" | grep -A 2 'Saturday' | grep 'icon' | sed -e 's/^ *//' -e s/'<div class=\"icon '//g -e s/' \"><\/div>'//g -e s/i-//g -e 's/\([0-9]*\).*/\1/' | tr -d '\n' | tr -d '\r'`
icon_sat=`printf "%.2d\n" $icon_sat`
icon_sun=`echo "$weather_weekend" | grep -A 2 'Sunday' | grep 'icon' | sed -e 's/^ *//' -e s/'<div class=\"icon '//g -e s/' \"><\/div>'//g -e s/i-//g -e 's/\([0-9]*\).*/\1/' | tr -d '\n' | tr -d '\r'`
icon_sun=`printf "%.2d\n" $icon_sun`

echo "https://vortex.accuweather.com/adc2010/images/slate/icons/"$icon_sat"-l.png" | xargs curl $weather_proxy --silent -o /tmp/weather_sat.png
echo "https://vortex.accuweather.com/adc2010/images/slate/icons/"$icon_sun"-l.png" | xargs curl $weather_proxy --silent -o /tmp/weather_sun.png

さいごに

他にもAccuWeatherには色々な予報がありますので、これまでに挙げたスクリプトの仕組みがわかれば様々な応用が可能でしょう。要はHTMLソースの中から欲しい情報をゲットするためのキーワードを拾ってHTMLタグを除去してテキストを整形しているだけですから、キーワードを確実に拾えるパターンを見つけさえすればOKです。