情報をスタック&可視化するサービスを作りたい

定期的に更新される情報。

数字だけで小難しかったり、単体で見てもわかりにくかったり。

そういうのをDBにスタックしてわかりやすく表示する機能を作ってみたい。

次の要素があると作りやすい。

  1.  1日1回定期更新
  2.  フォーマットが変わらない
  3.  意味のある数値が毎日変化する

今回は練習作としてFXのスワップ金利を対象に作ってみた。

LionFXのスワップ可視化 | Narumium Portal
ヒロセ通商のLionFX。そのスワップ金額をチャートにして見えるようにしました。お試しテスト機能なので急に変更したり停止したりします。

※スワップ金利とは2国間の金利差を日割りにして付与するものです。

 

仕組み

基本的にAWSで収まる設計にする。

概要 : CloudWatch -> Lambda -> DynamoDB <- Lambda <- API  <- S3

  • 入力
    1. CloudWatch Events – Schedule: daily  で1日1回Lambda関数を起動する
    2. Lambda関数でスクレイピング+DB格納
  • 出力
    1. S3に置いたHTMLファイルからAPI呼び出し
      • 普通にHTTP POST(GETしたかったがうまく設計できなかった)
    2. APIからLambdaを起動
      • Lambda関数と統合する
    3. DBから検索結果を返す
      • JSON形式でパススルー

 

可視化

次に持ってきたJSONをグラフ化する。

グラフ作成系のJavascriptは結構あるけど、どれも面倒な感じ。

今回は比較的簡単そうでデザインと名前が好みのChart.jsを使ってみる。

これは基本機能は簡単ではあったけど、ちょっと変えようとすると難しい。

頻繁に仕様が変わっているのか、ブログや紹介記事なんかはまったく当てにならなかったので公式ドキュメント以外は信用しない方がいい(Update関連は公式通りではうまくいかなかったけど)。

出来上がったチャートはこんな感じ。アニメーションが心地良い。

swap

 

さて何をつくろうかな。

 

AWSを使って欲しい情報を自動取得する

やりたいことの概要

技術的にはクローリングとかスクレイピングとか呼ばれている。

クローリングはWebを自動で徘徊する技術で、スクレイピングはさらに欲しいものだけ抽出するような感じ。

例えばポータルのほうで技術ニュースリンクをまとめてるのはこの技術を使っている。

方法は色々あるけどAWSで簡単にできそうなものを2つ実装している。

けどいまいちしっくりこないので現状をまとめつつ、どう運用するのが一番いいか考えてみる。

1つ目の方法はEC2にヘッドレスブラウザを入れてJenkinsで管理するやり方。

2つ目の方法はLambdaで起動してCloudWatchで管理するやり方。

どっちもスクレイピング自体はjavascriptを中心にした方法で実現している。

方法1

概要

■EC2+phantomjs+casperjs+Jenkins

EC2インスタンスを立ち上げてその中で好きなようにしようというスタンス。

phantomjsはヘッドレスブラウザでcasperjsはそのユーティリティ。

javascriptで実行できる画面描写のないブラウザを利用して情報を集める。

Jenkinsのジョブで収集スクリプトを起動したり、データを格納したりする。

メリット

インスタンス内でできることは何でもできるので拡張性が高い。

デメリット

インスタンスのメンテナンスが必要。EC2インスタンスは起動時間課金なのですでに運用しているインスタンスに相乗りしたり、必要に応じて起動/停止をしないとお金がかかる。

スクレイピング実行部分のサンプル

ブラウザとして巡回するので広告ページを待ったりしている。

ヘッダをいい感じに設定すれば特に待つ必要はないかもしれない。

方法2

概要

■Lambda+nodejs+cheerio-httpcli+CloudWatch

インスタンスを保持せずサービスとして使おうというスタンス。

cheerio-httpcliはnodejsのスクレイピング用モジュール。

LambdaにZIPを置いてevent sourceでCloudWatch Events – Scheduleを設定する。

メンテ不要でAWSの他サービスが使いやすい利点がある反面、Lambdaの制限がある。

特にマックス300秒の制約を気にしないといけないかも。

起動以外は取得、整形、格納なんかは全部Lambdaの中で行う。

メリット

実行時間のみ課金されるので使わないときのメンテが不要。

AWSの他サービスを使いやすい(IAM ロールの設定などが簡単)。

デメリット

Lambdaの制約内でやる必要があるので拡張性は低め。

スクレイピング実行部分のサンプル

書き方は色々あるけど一番簡単そうなPromise数珠繋ぎで書いている。

cheerio-httpcliに関しては作者さんがかなり丁寧に解説しているので使いやすかった。

まとめと感想

どちらも多少ニッチな知識が必要だけどjavascriptを多少知っていれば難しいとこはないし、ググればすぐに情報が手に入るので実装は簡単だった。

クライアントアプリケーションを途中でかませたりするような場合にはEC2内でやって、AWS内だけで済む処理なら基本Lambdaにするのがいいかなと思う。

今回の組み合わせの他にもEC2でnodejs動かしたり、Lambdaでphamtomjs動かしたりする例も見かけたけど特にそうする理由はなさそう。

kimonoみたいな専用サービスを使うとスクレイピング実施部分はほぼノータイムでできるんだろうけど、利用するサービスが増えると色々と面倒見ないといけなくなりそうなのでしばらくはAWSに依存してみようと思う。

 

AWSのDynamoDBをAPIでCRUD操作する

概要

DynamoDBをAPIで使えるようにした。

HTTP RequestでDynamoDBを操作する
何をするか外からAPIを叩いてデータをDBに保存してみたかったので試してみた。プラットフォームはAWSオンリー。以下のサービスを使って連携をしてみる。 API Gateway ...

次にAPIを通じてCRUD操作する方法を記録しておく。

基本的に公式ガイドを参照したけどわかりにくかったので他も色々見ながら試行錯誤した。

検索するといろんな情報(古い書き方、非推奨の書き方や別の言語での書き方)が入り混じっていて結構わかりにくかった。

create レコードを新規作成

payload.TableNameで指定したテーブルにpayload.Item内のJSONを新規登録する。

テーブル作成時にプライマリーキーに設定した属性は必須。

それ以外はJSONであれば自由。

また、プライマリーキーの値がすでにあるものであった場合はレコードそのものを上書きする。

read レコード1件取得

payload.TableNameで指定したテーブルにpayload.Keyにプライマリーキーを指定して1レコードを取得する。

 

update レコード1件更新

createで同一プライマリキーを指定したときとは違って、レコードの一部を変更できる。

payload.Keyで指定したレコードの更新を行う。

プライマリキー属性は更新できないので、その場合はdeleteしてcreateしなおす必要がある。

また、AttributeUpdatesで処理を定義することもできるが現在は非推奨とされている。

まずUpdateExpressionに式を記入する。

以下の4つの式が使えるがSET, REMOVEの他は基本使わない。

  • SET – 属性の追加、更新
  • REMOVE – 1 つ以上の属性を項目から削除
  • ADD – 数値とセットデータのみサポートするが、通常はADD ではなく SET が推奨される
  • DELETE – セットデータ(HashSet)からString配列で指定したものを削除

次にExpressionAttributeNamesに属性名を定義。直接UpdateExpressionで指定してもいいが予約語とかぶらないように注意する。

同様にExpressionAttributeValuesに代入する値を定義。

ReturnValuesは以下から選ぶ。

  • ALL_OLD – 更新の発生前に表示されたように、項目全体が返される。
  • ALL_NEW – 更新後に表示されるように、項目全体が返される。
  • UPDATED_OLD – 更新の発生前に表示されたように、更新した値のみが返される。
  • UPDATED_NEW – 更新後に表示されるように、更新した値のみが返される。

 

DELETEは今回の例だと使えないため割愛。

書き方 : DELETE HashSet [“a”,”b”,…]

delete レコード1件削除

プライマリキーを指定してレコードを削除する。

list レコード一覧取得

テーブルを指定してレコード一覧を取得する。

 

HTTP RequestでDynamoDBを操作する

何をするか

外からAPIを叩いてデータをDBに保存してみたかったので試してみた。
プラットフォームはAWSオンリー。
以下のサービスを使って連携をしてみる。

  • API Gateway
  • Lambda
  • DynamoDB

DynamoDBの用意

まずAWSでNoSQL DBサービスのDynamoDBの準備をする。
AWSのサービスはそれぞれテスト機能があるので一番深いところから用意していくとトントン進む気がする。

  1. サービス一覧からDynamoDBを選択する
  2. テーブルの作成ボタンを押す
  3. 好きなテーブル名、プライマリーキーを設定する。今回はtestテーブル、キーをidにした。

以上でDBの準備完了。簡単!

Lambda, APIの作成

LambdaはAWSで用意されているコードの実行サービス。
Java,Nodejs,Pythonコードを実行可能(今回はNodejs)。
なのでここには処理の実行部分を書く。
今後処理の内容を弄るときには基本的にここをでロジックを書く必要がある。
今回やりたいことはBlueprint(色々セットで作ってくれるテンプレートのようなもの)で用意されているのでそれを使う。

  1. サービス一覧からLambdaを選択
  2. Create lambda functionボタンを押す
  3. Select blueprintからmicroservice-http-endpointを選択
  4. Nameは好きな名前を入れる(今回はtestLambda)
  5. RoleでBasic with DynamoDBを選ぶと新規ページが開く
  6. そのまま右下の許可を押す
  7. Roleの選択肢にlambda_dynamoが追加されているの選択(今後は直接これを選ぶ)
  8. 右下のNextボタンを押す
  9. microservice-http-endpointで作成した場合にはそのままAPIが作成できるのでmethodをPOST,SecurityをOpenに変えてNext
  10.  確認画面でOKなら完了

これで準備完了なのでテストしてみる。

Lambdaのテスト機能を使う

Lambdaの左上のTestボタンを押すとeventの中身を直接入力してテストできる。

以下のJSONを入力してテストすると実際にレコードが登録される。

DynamoDBのtestテーブルを見るとレコードが追加されているのが確認できる。

RESTクライアントを使ったテスト

ブラウザの拡張でREST APIテスト用のアドオンがあるのでそれで実際の動作を確かめる。

restclient

URL:APIエンドポイント
Method:POST
Body:

結果

Dynamoのtestテーブルを見ると2つのテストで入れたレコードが確認できる。

dynamo

このままだとURLをたたけば誰でもデータベース操作できてしまうのでLambdaの処理を絞ったり、APIに認証を設定したりする必要がある。