静的webページの読み取り(Rails&Nokogiri)

あるwebページの情報を取り込んできて自分の都合が良いように変換して表示したい時があると思います。 その方法についてみていきたいと思います。

環境

概要

スクレイピングを行い、webページの情報を読み取る。

読み取った情報を、加工して表示する。

読み取り対象は、東京ヤクルトスワローズの投手成績のページ。

https://www.yakult-swallows.co.jp/players/stats/pitcher

読み取り後は、防御率などの項目ごとに上位3名を抜き出して表示する。

手順概要

  • URLにリクエストを飛ばし、該当ページのデータを取得する
  • Nokogiriを用いて、取得したページデータから必要な項目を取得し、配列に格納する。
  • 必要なデータを格納した配列の中から、上位3名のデータを抜き出して表示する。

手順詳細

[controller]

include RecordsHelper

def  index 
  ##ヘルパー呼び出し。投手成績のページのデータを取得したものを変数に代入
  pitcher_all_records = get_pitcher_all_records
 ##ヘルパー呼び出し。ピッチャーの情報を配列に格納したものを変数に代入
  pitchers_info = get_pitchers_info(pitcher_all_records)

 ##ピッチャー情報の配列から、防御率が低い3人を抽出
  @era_info = pitchers_info.sort_by { |_, era, _, _, _ | era }[0..2]
      ##ピッチャー情報の配列から、ホールド数が多い3人を抽出
  @hold_info = pitchers_info.sort_by { |_, _, hold, _, _ | hold }.reverse[0..2]
  ##ピッチャー情報の配列から、セーブ数が多い3人を抽出
  @save_info = pitchers_info.sort_by { |_, _, _, save, _ | save }.reverse[0..2]
  ##ピッチャー情報の配列から、勝率が高い3人を抽出
  @win_info = pitchers_info.sort_by { |_, _, _, _, win | win }.reverse[0..2]

[records_helper]

# require 'open-uri'
# require 'nokogiri'

##投手成績のページのデータ取得を行うメソッド
def get_pitcher_all_records
  ##とってきたいページのURLを指定する      
  url = "https://www.yakult-swallows.co.jp/players/stats/pitcher"
  charset = nil
  html = open(url) do |f|
    charset = f.charset
    f.read
  end
  Nokogiri::HTML.parse(html, nil, charset)
 end

##取得したデータから、ピッチャーの欲しい情報だけを取り出して格納するメソッド
def get_pitchers_info(records)
  pitcher_infos = []
  records.css("tr").each_with_index do |player, i|
  next if i == 0
    pitcher_info = []       
    #名前
    pitcher_info << player.css("td")[0].text
    #防御率
 ##指定投球回数以上投げている選手だけ格納対象にする
    if player.css("td")[17].text.to_f > 143
      pitcher_info << player.css("td")[1].text.to_f
    else
    ##指定投球回数以下の選手は、ランキング対象外にするため防御率としてはなさそうな大きな値を入れておく
    ##(値でのソート時のために、文字列("---"とか)ではなく数値を入れておく)
      pitcher_info << 99999           
    end
      
    #ホールド数     
    ##"---"が入っているとソート時に厄介なので数値を代入
    if player.css("td")[11].text == "---"    
      pitcher_info << -1    
    else     
      pitcher_info << player.css("td")[11].text.to_i    
    end
           
    #セーブ数 
    if player.css("td")[13].text == "---"
      pitcher_info << -1
    else
     pitcher_info << player.css("td")[13].text.to_i
    end
          
    #勝率    
    if player.css("td")[14].text == "---"
      pitcher_info << -1
    else
     pitcher_info << player.css("td")[14].text.to_f
    end
    pitcher_infos << pitcher_info
  end

  //railsだと普通return書く必要ないはずですが、うまく呼び出し先に返せなかったため明記
  return pitcher_infos
end

[view(index.html.erb)]

<h3>ピッチャーランキング</h3>
<h5 class="item">防御率(規定投球回数以上の選手のみ)</h5>
<table>
  <% @era_info.each do |era| %>
    <% if era[1] != 99999 %>
      <tr>
        <td><%= era[0] %></td>
        <td><%= era[1] %></td>
      </tr>
    <% end %>
  <% end %>
</table>
        
<h5 class="item">ホールド数</h5>
  <% @hold_info.each do |hold| %>
    <div>
      <span><%= hold[0] %></span>
      <span><%= hold[2] %></span>
    </div>
  <% end %>
  <h5 class="item">セーブ数</h5>
  <% @save_info.each do |save| %>
    <div>
      <span><%= save[0] %></span>
      <span><%= save[3] %></span>     
    </div>  
  <% end %>   
  <h5 class="item">勝率</h5>  
    <% @win_info.each do |win| %>
      <div>
        <span><%= win[0] %></span>    
        <span><%= win[4] %></span> 
      </div>    
     <% end %>
  </div>

その他

ここで記述した手法であらゆるページがスクレイピングできるわけではないようです。

スクレイピングしたいページについて、右クリック⇒ページのソースを表示 を行って、その状態で見える範囲ならこの手法ができるようです。

JavaScriptなどで動的に描写されたページだと、Seleniumなどほかのツールも一緒に用いないとダメなようです。また調べたらまとめようと思います。