Rails + Nokogiri + Vue.jsでスクレイピング & SPAを作った

Rails + Nokogiri + Vue.jsを使ってシングルページアプリケーションを作りました。


史上5頭目の三冠牝馬アーモンドアイの競走成績表を作成してチェックできるアプリケーションです。
ネット競馬.comをスクレイピングしたデータをもとにRailsのモデルオブジェクトを作成し、非遷移で各データを表示します。

実用性、皆無!でも楽しかったからいいじゃない。



f:id:naito-coding0322:20181202134437g:plain
完成したもの



前回作ったはてなブログ風アプリケーションでは見た目部分をほぼBootstrapで実装しましたが、今回はCSSをしっかり書きました。
JavaScript, Vue.js, スクレイピング, CSSと初めて挑む分野が多く必死でしたがその分学べたことも多かったので、ページを分けてまとめました。




※このアプリケーションで取得しているデータは、競馬.comに登録することなく閲覧できるものに限られています

個々の記事へのリンク


axios.POSTした配列オブジェクトをRailsのモデルオブジェクトとして保存する

データ削除後の表示を非遷移で更新する

Railsの処理が失敗した時に失敗JSONを返すことでVue.jsの処理を分岐させる

モーダルウィンドウを使うときのHTML構成

その他



全体の設計

はじめは「戦績一覧のレース名をクリックしたときに非遷移でレース結果を表示できたら面白いしJSの勉強になるぞ!」という思いつきで始めました。


モデル
  • 開催日、馬場状態、レース名などのカラムを持つRaceモデル
  • 着順、馬名、騎手、その他結果データのカラムを持つRecordモデル

の2つのモデルを作成。


コントローラ


・ Keiba_Dbコントローラ
 ・index 処理はなし。メインとなるページ


Api::KeibaDbコントローラ
 ・index  すべてのRaceオブジェクトを返す
 ・destroy Raceオブジェクト(と関連付いている全てのRecordオブジェクト)を削除する
 ・create 選択したレースに紐づく全てのRecordオブジェクトを返す
 ・show アーモンドアイのnameカラムに紐づく全てのRecordオブジェクトを返す


Api::GetRaceRecordsコントローラ
 ・new params[:レース名]を元にスクレイピングして一つのRaceオブジェクトと複数Recordオブジェクトをnewして返す
 ・create Vueから送られてきたデータを元に一つのRaceオブジェクトと複数Recordオブジェクトを保存する


Railsのモデルオブジェクトとしてデータを作成&保存する部分は後から実装したため、コントローラの構成が非常に分かりづらくなってしまったのが反省点です。命名もマズい。

上から

  • Homeコントローラ
     ・index
  • Api::Homeコントローラ
     ・race_index
     ・destroy
     ・records_index
     ・race_show
  • Api::Race_Recordsコントローラ
     ・new
     ・create


とかにすると分かりやすいかなと今考えましたが、この部分はRailsの思想というかMVCモデルにおけるコントローラの役割について調べ直してから着手することにします。



画面上の動き


データを見る

f:id:naito-coding0322:20181202145808p:plain


画面上にテーブルを2つ配置し、ページがロードされた状態ではRecordsにはアーモンドアイの成績のみをApi::KeibaDb#showから表示しておき、レース名をクリックすると右のテーブルにそのレースに紐づく全てのRecordをApi::KeibaDb#createから取得して表示します。



データを作る

f:id:naito-coding0322:20181202160153p:plain


データ追加ボタンを押すとモーダルが開きます。
レース名を入力してGETボタンを押すとApi::GetRaceRecords#newにレース名が送られます。


newアクション内の処理は以下の通り。

ネット競馬.comのアーモンドアイ戦績一覧ページで、一致するレース名の href属性からレース詳細ページのURLをパースして取得。

そのページの中で、
・レース名や開催日などをパースしてRaceオブジェクトをnew
・tableタグ内の各項目をパースして取得し、Recordオブジェクトの配列をnew

をして、それぞれをJSONで返します。


Vue.js側では

axiosでJSONをGETして、それぞれのデータをraceConfirmationrecordsConfirmationに格納。
テーブルの各欄に並べたinputにv-modelで上記2つのデータを表示してデータの確認という形でモーダル内で提示します。


f:id:naito-coding0322:20181202154448p:plain


内容を確認してCREATEボタンを押すと、raceConfirmationrecordsConfirmationの中身をaxiosがRailsApi::GetRaceRecords#createにPOSTし、Railsがそのデータを保存します。


間にユーザーによるデータ確認が入るのでApi::GetRaceRecordsコントローラでnewしたデータをそのまま保存するわけではない、という所がポイントです。



データの削除は単純で、選択したレースのIDがApi::KeibaDb#destroyに送られて削除されるだけです。ただ削除後に更新されたレース一覧を非遷移で更新させる部分で少し詰まりました。





作業日程


7日間(だいたい80時間)
日曜日にジャパンカップを見てうおー!と思ってからそのうおー!を一週間保ち続けたまま完成まで突っ走りました。突っ走れたけど初めにもう少し細部まで仕様と設計を固めてから作ればよかった。


f:id:naito-coding0322:20181202164417p:plain


今回はあとでブログにまとめやすいように、こんな感じでブランチ名とコミットメッセージと共に作業工程を管理しました。
これはとても役立ちました。こういう管理のやり方についてももっと知りたくなった。まず企業に入ろう。



感想



・今回はJavaScript, Vue.js, Nokogiri, CSSと初めて触れるもの多かった。
基礎を学ぶ→実際に動くものを作る→ブログにまとめる→基礎をやった時に不明瞭だった所や応用知識を使って機能改善&リファクタリング→再びブログにまとめる
このサイクルでやってみたら非常に多く吸収することができた。


・「2つ目の言語を学ぶ時に、その言語自体を学ぶのと並行して"なるべく速く新しい技術を身につける方法"を確立できると幸せになれそうだな」と思っていたけど、今回そういう型(フレームワークだ!)がある程度固まったのでとても良かった。いろんなノウハウを身につけてもっと改良したい。


・スピード感は大事だけど、もう少し細部まで仕様と設計を考えてから作り始めたほうがいいかも。CSSも何も考えずに書くとゴッチャゴチャになるのでおおよそのHTMLの階層イメージとか考えておく

・ボタンのデザインとかはコピペで利用できるように公開されてるものが多々あるので、あまり深入りしすぎない。ただコピペしたものを改変できるように基礎知識は抑えておく。box-sizingなどの配置系とか。


RailsとVue.jsのような組み合わせで開発するとき、それぞれに担当させる領域をどう線引きするかを勉強する。


・プログラミングはめちゃくちゃ楽しいので朝起きて飯食わずに夜までコード書くことも出来るけど、できればご飯はちゃんと食べる。睡眠は削らない。


・アーモンドアイはきっとめちゃくちゃ強い