前回同様結論こんな感じのコードで実装しています
# frozen_string_literal: true
class TradingValueHighRankingsController < ApplicationController
require 'open-uri'
URL = 'https://finance.yahoo.co.jp/stocks/ranking/tradingValueHigh?market=all&term=daily'
def show
@ranking = load_ranking
end
private
def load_ranking
htmls = read_htmls
docs = parse_htmls(htmls)
pick_ranking_data(docs)
end
def read_htmls
# 200位まで取得するため対象サイトの4ページ目までを読み込む
(1..4).each_with_object([]) do |i, array|
array << URI.parse(URL + "&page=#{i}").open.read
end
end
def parse_htmls(htmls)
htmls.each_with_object([]) do |html, array|
array << Nokogiri::HTML.parse(html)
end
end
def pick_ranking_data(docks)
docks.each_with_object([]) do |doc, array|
doc.css('table tbody tr').each do |element|
array << element.at_css('ul li').children.text
end
end
end
end
上の行から
# frozen_string_literal: true
これは文字列を変更できないようにするためのマジックコメントになります。
Rubyは仕様上定数で定義したものの変更が可能なため、このようなマジックコメントをつけておくと安全だと思います。
class TradingValueHighRankingsController < ApplicationController
require 'open-uri'
URL = 'https://finance.yahoo.co.jp/stocks/ranking/tradingValueHigh?market=all&term=daily'
クラス名の定義とopen-uriというruby標準のライブラリを読み込んでます。
そしてURLという定数にスクレイピングする対象のURLとクエリパラメータをセットしています。
クラス名は突貫で書いてしまったのでちょっとセンスないなと思っているので今後リファクタしたいですよね。。名前空間とか使って今後の機能拡張にも対応できるようにしたいところです。
def show
@ranking = load_ranking
end
ここはrailsのアクションを呼び足してる個所ですね。このコントローラーファイルのアクションですべて完結させるのもいいんですがこの記事に感銘を受けて細かい関数を使ってシンプルに書くように心がけてみました(この辺は個人の好みによると思います。)。今後アクション数が増えたとしてもCRUDアクションのみしか増やさない方針です。(というかこのアクションindexが適切だったと思ってるのでそこもリファクタ予定w)
なのでprivate内の
- load_ranking
- read_htmls
- parse_htmls
- pick_ranking_data
を使って@rankingを定義しています。
各関数のやってること
load_rankingについては各関数を呼び足しながらpick_ranking_dataを最終的に呼び出す
read_htmlsでは
def read_htmls
# 200位まで取得するため対象サイトの4ページ目までを読み込む
(1..4).each_with_object([]) do |i, array|
array << URI.parse(URL + "&page=#{i}").open.read
end
end
1~4の範囲オブジェクトをループして4ページ分のヤフーファイナンスの対象ページ(今回であれば売買代金ランキング)を読み込んでくることをしています。
&page=#{i}で1~4までのページ数を指定しています。
※open-uriって結構webで調べると下記のような書き方をしてるのが多いですが今だとエラーになるので注意です。ruby3.0以降からですが
open(URL).read
じゃあっていうので
URI.open(URL).read
とかくとrubocopでリスクのある書き方だからよせと言われて今の書き方に落ち着きました。
parse_htmlsでnokogirigemを利用してHTMLを取り回しやすいように解析しています。
これも4ページ分HTML情報をとってきてるのでその数分ループさせています。
def parse_htmls(htmls)
htmls.each_with_object([]) do |html, array|
array << Nokogiri::HTML.parse(html)
end
end
最後にpick_ranking_dataを呼び出して所定のテキスト情報(今回の場合は銘柄コード)を取り出して配列として返却する流れになっています
def pick_ranking_data(docks)
docks.each_with_object([]) do |doc, array|
doc.css('table tbody tr').each do |element|
array << element.at_css('ul li').children.text
end
end
end
.css?at_css?って方はこの記事がおすすめです。これ見れば大概のwebページの情報を抜いてくることができると思います。
まとめ
いかがでしょうか?っていうほどのものでもないのですが、解説してみるとコードの不完全さがまだまだあるなと思うので近々のうちにリファクタしたものも共有できればなと思ってます!それでは!
コメント