SolitonNKを使ってオープンデータを活用するぞ ステップ1
近年、誰もが自由に使える公共的なデータが、種々の官民組織から公開されています。最近のトピックスで言えば、厚労省が「新型コロナウイルス感染症の現在の状況と厚生労働省の対応について」として、日本国内のPCR検査状況、患者数、退院・療養解除者数、死者数を日々公開しています。
これらのデータの多くはWebページないしWeb APIで公開されているので、「http
でデータを取得し、SolitonNK
のインデクサーに蓄積し、分析にかけられるようにしたい」、という要求に応えるべく、そのやり方を探ってみました。SolitonNK
初級の筆者には、トライ&エラーとなって苦戦することになってしまいましたが、その分難所を押さえつつステップバイステップで解説できるものと思います。ということで、本記事はその第1ステップです。
「http
でデータを取得し、SolitonNK
のインデクサーに蓄積」するなら、当然 http
インジェスターの出番だねと思えたのですが、http
インジェスターには「他のウェブサイトからデータを取ってくる」という機能は備わっていません。http
インジェスターが公開しているWebインターフェースに、POST
メソッドで入力されたデータを、インデクサーに蓄積するのが、http
インジェスターの機能なのです。
とういうことは、、、、どうやって他サイトでWeb公開しているデータをとってくればよいの?という問題から取り組むことになります。SolitonNK
に内蔵されているgravwell
エンジンのブログの解説を見ると、
#!/bin/sh
curl "https://www.data.jma.go.jp/obd/stats/data/mdrr/pre_rct/alltable/pre1h00_rct.csv" | curl SolitonNK.example.jp:80/snk -X POST -d @-
という具合にやればデータを取り込めるので、cron
でこのコマンドを定期的に走らせばよいと記述されています。
が、上記の方法を実行するためには、SolitonNK
以外にもう1ホスト常時稼働サーバを作らねばならないということになり、それでは運用コストが跳ね上がってしまいます。そこで、SolitonNK
のAnko
スクリプトで、上記curl
コマンドの機能を作り、スケジュール検索機能で定期的にそれを起動させるという方針でやってみることにしました。
Anko
スクリプト
さて、気象庁が公開している「最新の気象データ」CSV
の 「時間降水量 最新データ」: https://www.data.jma.go.jp/obd/stats/data/mdrr/pre_rct/alltable/pre1h00_rct.csv をとってきて、http
インジェスターの受け口として用意した solitonNK.example.jp:80/snk
にデータを http POST
するAnko
スクリプトは次のように書けます。
最初に書いたスクリプト(一見動作しますが、……)
var http = import("net/http")
var strings = import("strings")
var buf = import("bytes")
var url1 = "https://www.data.jma.go.jp/obd/stats/data/mdrr/pre_rct/alltable/pre1h00_rct.csv"
var url2 = "http://solitonNK.example.jp/snk"
func Process() {
req, _ = http.NewRequest("GET", url1, nil)
req.Header.Add("Content-Type", "application/octet-stream")
resp, _ = http.DefaultClient.Do(req)
# resp.Body.Close()
setEnum("Status1", resp.Status)
req, _ = http.NewRequest("POST", url2, resp.Body)
req.Header.Add("Content-Type", "application/octet-stream")
resp, _ = http.DefaultClient.Do(req)
resp.Body.Close()
setEnum("Status2", resp.Status)
return true
}
url1
で指定したサイト(気象庁サイト)からGET
で取ってきたデータのresp.Body
は、io.Reader
型のデータなのですが、SolitonNK
からはAnko
の"io/ioutil"
モジュールも、"io"
モジュールも使えないため、ioutil.ReadAll()
も、buf.ReadFrom()
も、io.Copy()
も使えないことから、resp.Body
は直接POST
のbody
に持っていく他はありません。
【ちょっと待て!1】 resp.Body.Close()
が1回しかできてないのは?
goのnet/httpでのやらかし事例などを読んで考えるに、2カ所に対してコネクションを張りながら、1回しかコネクションを閉じてないということは、反復使用しているうちにリソースを使い尽くしてしまうはずです。2つのサイトへのコネクションはそれぞれ閉じないといけません。
2回目に書いたスクリプト(やっと使えるようになりました)
http.DefaultClient.Do(req)
のレスポンスを格納する変数を2つ用意して、それぞれ別々に閉じられるようにスクリプトを次のように書き直しました。
var http = import("net/http")
var strings = import("strings")
var buf = import("bytes")
var url1 = "https://www.data.jma.go.jp/obd/stats/data/mdrr/pre_rct/alltable/pre1h00_rct.csv"
var url2 = "http://solitonNK.example.jp/snk"
func Process() {
req, _ = http.NewRequest("GET", url1, nil)
req.Header.Add("Content-Type", "application/octet-stream")
resp_get, _ = http.DefaultClient.Do(req)
setEnum("Status1", resp_get.Status)
req, _ = http.NewRequest("POST", url2, resp_get.Body)
req.Header.Add("Content-Type", "application/octet-stream")
resp_post, _ = http.DefaultClient.Do(req)
resp_post.Body.Close()
setEnum("Body", resp_post.Body)
resp_get.Body.Close()
setEnum("Status2", resp_post.Status)
return true
}
上記のAnko
スクリプトをエキストエディタで記述したら、適当な名前(図例ではGetJMA4.ank.txt
)で保存します。そして、SolitonNK
の「リソース」に手元に保存したスクリプトファイルを適当な名前をつけて(例ではgetjma2
)追加します。
図1:メニューから「リソース」を選択
図2:リソース管理画面の右上の追加ボタン(初回は真ん中の「リソース追加」ボタンでも良い)からリソース追加
図3:新規リソース追加の際は、リソースの名前と、リソースの説明(使用方法など)を記述する
図4:リソース登録後の画面
http
インジェスター
Anko
スクリプトが用意できたら、次にデータの受け口となるhttp
インジェスターを稼働させます。
各種インジェスターの設定は、SolitonNK
の管理サイトの方にログインします。
図5:管理サイトのログイン画面
管理サイト画面左上の「サービス」タブによって開いた「データ収集/分析」画面の、「HTTPサーバー」という欄がhttp
インジェスターの設定欄です。http
インジェスターが上記のAnko
スクリプトからのhttp POST
によって受け取るURLをここで設定します。Anko
スクリプトでデータをPOST
させた"SolitonNK.example.jp:80/snk"
の、ポート番号と/
以下のURLをここに記述します(デフォルトはポート80
、URLは/snk
です)。
図6:http
インジェスターの設定画面
HTTP
サーバーを「起動する」の欄にチェックを入れたら、左下の「適用する」ボタンを押して、SolitonNK
のサービスを再起動させると、http
インジェスターが稼働するようになります。再起動後、画面右上の「設定を保存」をクリックして、設定保存するのも忘れないようにしてください。
図7:設定の反映と設定の保存
ここで試しに、検索欄で tag=* limit 1 | anko getjma2 | table
を実施すると、Status1の欄とStatus2の欄が両方とも200 OK
になっています。これは、気象庁サイトからのhttp GETの実行ステータスも、httpインジェスターへのhttp POSTの実行もステータス「正常」だったことを示しています。
図8:Anko
スクリプトの実行確認
定期実行(検索のスケジュール)
さて、気象庁の「時間降水量 最新データ」 https://www.data.jma.go.jp/obd/stats/data/mdrr/pre_rct/alltable/pre1h00_rct.csv
は直近1時間のデータです。このデータを1時間毎に定期的に取得してSolitonNKに蓄積すれば、降水量の変化データが蓄積できることになります。上で作ったAnko
スクリプトを1時間に1回実行する、スケジュール検索を設定しましょう。
まずSolitonNKのメニュー画面から「スケジュール検索」を選択します。
図9:メニュー画面から「スケジュール検索」を選択
「スケジュール検索」画面で、右上または中央の「追加」ボタンで、新規のスケジュール検索を設定できます。
図10:「スケジュール検索」画面
定期的に実行させたい検索文(例では、tag=http limit 1 | anko getjma2 | table
)を記入し、「スケジュール」欄で検索の起動スケジュール(例では毎時0分に実行)を設定します。この設定記述方法はcron
の方法と同じです。「時間範囲」は、検索を起動してから終えるまでの時間の指定、その他にこの登録しようとしてるスケジュールについて名前と説明文を記述することができます。検索スケジュールの設定が記入できたら、「保存」ボタンで登録できます。登録とともにこの検索スケジュールを実施する場合には、「保存後に実行」にチェックを入れます。
検索スケジュールを登録すると、図のような画面になります。
図12:検索のスケジュール登録後画面
検索のスケジュール登録後画面の、「最終実行」の欄の"Results"欄をクリックすると実行された検索の結果が表示されます。
【ちょっと待て!2】:検索文だけではなく、ここではスクリプトを登録することができます。ここで登録されるスクリプトは検索から呼び出すスクリプトと同じ制限なのでしょうか?ひょっとしてもっと自由度高く簡便にスクリプト記述できるのではないでしょうか?
「スケジュール検索」でスクリプトを登録する場合、スクリプト記述方法が異なります。まず、func Process(){
~return}
を使わなくて良くなります。検索結果を返さなくて良い(表示インターフェースがない)ので、setEnum()
は不要です。そして、httpGet(url)
, httpPost(url, content-type, body)
, sendMail(hostname, port, username, password, from, to, subject, message)
が使えるようになります。なお、"io/ioutil"
モジュールも、"io"
モジュールも使えないという制限は変わりません。そこで、スクリプトを「スケジュール検索」に登録することを前提に、書き直してみましょう。
3回目に書いたスクリプト(「スケジュール検索」でスクリプトを登録する場合用)
var url1 = "https://www.data.jma.go.jp/obd/stats/data/mdrr/pre_rct/alltable/pre1h00_rct.csv"
var url2 = "http://solitonNK.example.jp/snk"
body, err = httpGet(url1)
httpPost(url2, "text/html; charset=shift_jis", body)
# sendMail("localhost", 25, "", "", "[email protected]", ["[email protected]"], "テスト", "成功"+body)
とってもシンプルに書けてしまいます。上で苦労したのは何だったんでしょう……。なお、httpGET()
で得た内容をメールで確認したい場合は上のスクリプトで、コメントアウトを外していただければ、メール送信してくれます。
なお、smtp
サーバにlocalhost
を指定してメール送信にすることについては、特に管理設定は要りません。
【ちょっと待て!3】:インデクサーに蓄積された内容を確認してみると……
気象庁の時間降水量 最新データは図のように日本全国各地の気象データがcsv
形式で記載されています。
図14:気象庁の時間降水量 最新データ
これがSolitonNK
のインデクサーにきちんと蓄積されているかtag=http limit 1 | table
として確認してみましょう。あれれ?酷い文字化けです。(メール送信した場合もデータ部分は文字化けしています)
せめて、csv
の元の表の形に整形できないかと思ってtag=http limit 1 | csv [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] | table 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
とやってみましたが、最初の行が表示されるだけです。
【ステップ2への課題】:文字コードのハンドリングはどうすればよいのか。
SolitonNK
はマルチバイト文字を内部的にはUnicode
で、入出力はUTF-8
でハンドリングしています。一方、気象庁サイトのデータはS-JIS
で記述されているので、コード変換が必要となります。
ということで、ここまで外部のWebの情報をインデクサーに蓄積するまでをステップ1の本記事とします。そして、文字化けを修正し、インデクサーから分析できる形で抽出するのが、次回記事以降の課題ということにして、ステップ2の記事にご期待ください。
レファレンス
Soliton NKを試したい人はの下の Soliton NK 公式サポートサイトでお申し込み下さい。
Soliton NK について、今後記事を追加していく予定です。
Author And Source
この問題について(SolitonNKを使ってオープンデータを活用するぞ ステップ1), 我々は、より多くの情報をここで見つけました https://qiita.com/kakawai/items/fd94a71ddac73190d4ae著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .