しばたテックブログ

気分で書いている技術ブログです。

PowerShellでwttr.inを表示してみる

はじめに

先日Twitterのタイムラインを見ててwttr.inというcurlコマンドで各地の天気予報が見れるwebサービスがあることを知りました。

以下の図はcygwin上のcurlコマンドでの実行例ですが、こんな感じで各地域の天気を知ることが出来ます。

f:id:stknohg:20160222192220p:plain

地味に便利なwebサービスです。

で、curlコマンドでいけるならPowerShellでもなんとかできんのかと思い調べてみたのが今回のエントリの内容になります。

PowerShellでwttr.inを表示してみる

結論から先に書くと、Windows 10 TH2(1511)以降のOSであればPowerShellでwttr.inを表示する事ができます。

PowerShellでwttr.inを表示させるには以下に示す2つの問題に対処する必要があり、ここからはその問題点と対処法について書いていきます。

wttr.inの実装について

調査に先立ってwttr.inの実装について軽く触れておきます。
wttr.inGithubでソースが公開されておりFlask製のアプリであることがわかります。 また、天気予報の表示にはgoのコマンドラインツールであるwegoが使われています。

UserAgentの設定

最初に何も考えずにPowerShellのcurl(Invoke-WebRequest)でアクセスしてみます。
すると以下の様になりHTML形式の文字列が返されてしまいます。

# Contentプロパティにアクセスしないといけないのはちと面倒ですが仕方ないですね...
(curl http://wttr.in/).Content

f:id:stknohg:20160222192627p:plain

wttr.inではUserAgentによってHTMLを返すかコンソール用の文字列を返すか判定しており、UserAgentにcurlwgethttpieが含まれている場合にコンソール用の文字列を返す様になっています。
ですので、以下の様にUserAgentを指定してアクセスする必要があります。

# -UserAgentを "curl"にしてアクセス
(curl http://wttr.in/ -UserAgent "curl" ).Content

ANSIエスケープシーケンスコードの設定

UserAgentを指定してアクセスすることでコンソール用の文字列を返すことができる様になりましたが、今度は以下の様に色付きの部分のANSIエスケープシーケンスコードがそのまま表示されてしまいます。

f:id:stknohg:20160222192701p:plain

Windows10までのWindows環境ではANSIエスケープシーケンスコードは表示することが出来ず、ANSICON等のツールで何とか頑張るしかありませんでした。

しかし、Windows 10 TH2のアップデートによりConsole Hostが改善されWin32 APIで設定することによりCmd.exeやPowerShellコンソールでANSIエスケープシーケンスコードを扱える様になりました。
詳細については以下の記事を参照してください。

Nivot Ink | Windows 10 TH2 (v1511) Console Host Enhancements

この記事から抜粋した以下のコードを実行するとPowerShellコンソールのモードを変更してANSIエスケープシーケンスコードが使える様になります。

#
# Windows 10 TH2(1511)以降でのみ有効
#
# Win32 API定義をAdd-Typeする
Add-Type -MemberDefinition @"
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetStdHandle(int handle);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool GetConsoleMode(IntPtr handle, out int mode);
"@ -Namespace Win32 -Name NativeMethods
# コンソールモードを変更
$Handle = [Win32.NativeMethods]::GetStdHandle(-11) #  stdout
$Mode = 0
$Result = [Win32.NativeMethods]::GetConsoleMode($Handle, [ref]$Mode)
$Mode = $Mode -bor 4 # undocumented flag to enable ansi/vt100
$Result = [Win32.NativeMethods]::SetConsoleMode($Handle, $Mode)

また、コンソールのフォントがLucida Consoleでないと表示がずれてしまうので、とりあえずCHCP 437を実行してコンソールの表示フォントも変えておきます。

# Lucida Consoleフォントを設定画面に出すためにchcp 437しておく。
# フォントの変更は設定画面からやってください
chcp 437

その上で再度curlコマンドを実行すると、一部の色が表示されていない*1ものの、ANSIエスケープシーケンスコードを処理した内容を表示することが出来ます。

# コンソールモードを変更後curlでアクセスする
(curl http://wttr.in/ -UserAgent "curl" ).Content

f:id:stknohg:20160222192925p:plain

最後に

PowerShellでも頑張ればwttr.inを表示出来ることがわかりました。
Windows 10でない環境の場合は最初の例の様にCygwinを使うのが楽で正確だと思います。

【2016/04/15追記】もう一つの方法

Redditの有志達の間でwttr.inを表示させる別の方法が話題になってました。

ttps://www.reddit.com/r/PowerShell/comments/4e5eyi/making_wttrin_working_with_powershell/
(何故かわかりませんがRedditへのリンクをはるとはてなに怒られるのでh抜きの懐かしい感じにしておきます。)

によると、

(curl -Uri "http://wttr.in/").ParsedHtml.Body.FirstChild.OuterText

とすることで、HTMLの結果から必要な部分を抜き出してコンソールに表示させています。
この方法だと色情報は消失してしまいますがWindows 10でなくても動作しますね。

実行結果はこんな感じです。

f:id:stknohg:20160415175140p:plain

【2016/08/03追記】Anniversary Update(1607)

本日Windows 10 Anniversary Update(1607)が来ました。

こちらの更新を適用すれば標準でエスケープシーケンスがサポートされますので、何もせずとも、

(curl http://wttr.in/ -UserAgent "curl" ).Content

curlするだけでよい感じに表示してくれます。

f:id:stknohg:20160803223044p:plain

*1:どうもまだ完全なサポートはされていない様です...