地方でリモートワーク in Iwate

東京の受託開発会社でリモートワークしてます。

スクレイピングことはじめ、Rubyで先物の4本値を取得する

スポンサーリンク

f:id:ihatov08:20170314155730j:plain

先物投資をやっているのですが、毎日4本値を調べるのが面倒。 スクレイピングの練習がてら取得するスクリプトを書いてみました。

スクレイピングを使えば、取得したい情報を抜き出して、好きな形に整形してメールでもslackでもブログに投稿でも好きなことができます。

プログラミングで身近に実利を感じやすい分野でもあります。

参考書籍はこれ。

Rubyによるクローラー開発技法

Rubyによるクローラー開発技法

Rubyでクローラ、スクレイピングを学びたいと思ったらこれ一択。

取得してくるサイトは松井証券の先物情報ページです。

先物市況 | マーケット情報 | 松井証券

このサイトでから取得したいのは、225先物とtopixの4本値

  • 始値
  • 高値
  • 安値
  • 終値(現在値)

の4つです。

このページは単純なHTMLなので、HTMLを解析するだけで情報が取得できます。

方法としては

  • wgetを使って正規表現で抜き出す
  • HTMLパーサーを使って情報を抜き出す

2通りの方法が考えられます。

HTMLパーサーを使った方が簡単なので、今回は後者の方法を選択します。

HTMLをパースするgemはnokogiriを使います。

github.com

まずはkonogiriのインストール。

gem install nokogiri

うまくいかない場合は公式tutorialを見ながらinstallしてください。

pryで色々叩きながら試します。 まずはnokogiriを使える状態にします。

[1] pry(main)> require 'nokogiri'
=> true
[2] pry(main)> require 'open-uri'
=> true

取得したいページがあるurlを変数に入れておきます。

[3] pry(main)> url = 'http://finance.matsui.co.jp/stocks/matsui/contents/futuresList.aspx'
=> "http://finance.matsui.co.jp/stocks/matsui/contents/futuresList.aspx"
[4] pry(main)>

nokogiriにかけます!

doc = Nokogiri::HTML(open(url))

docの中に解析されたHTMLが格納されています。

解析されたHTMLからどうやって4本値を取得するか?

今回はxpathを使います。

xpathを調べるにはchromeを使えば簡単です。 1. 取得したい情報の上で検証を押す 2. 青くハイライトされたHTMLの上でcopy→xpath

を押せばクリップボードにxpathがコピーされた状態になります。

このxpathをつかってdocの中から抜き出したい要素をしていします。

これは現在地をぬきだす場合です。

doc.xpath('/html/body/div[1]/main/div[2]/article/div[2]/div[2]/div[1]/div/div[1]/div/table/tbody/tr[1]/td/b')

これだとnokogiriのオブジェクトが返ってくるのでinnner_textメソッドを使って情報のみ取得します。

[6] pry(main)> doc.xpath('/html/body/div[1]/main/div[2]/article/div[2]/div[2]/div[1]/div/div[1]/div/table/tbody/tr[1]/td/b').inner_text
=> "19,480.00"

カンマと小数点は不要なので、正規表現で削除します。

[8] pry(main)> doc.xpath('/html/body/div[1]/main/div[2]/article/div[2]/div[2]/div[1]/div/div[1]/div/table/tbody/tr[1]/td/b').inner_text.gsub(/(\d{0,3}),(\d{3})/, '\1\2').to_i
=> 19480

できあがったコードはこれです。

slackのweb hook urlはご自分のものに差し替えてください。

225先物とtopix先物の4本値を取得してslackに通知するスクリプトです。 また、yahooからトヨタとSUBARUの株価の4本値を取得しています。

require 'nokogiri'
require 'open-uri'
require 'pry'
require 'slack/incoming/webhooks'

class FourValue
  def initialize(url, name)
    @doc = Nokogiri::HTML(open(url))
    @name = name
  end

  attr_accessor :name, :opening_price_path, :high_price_path, :low_price_path, :closing_price_path

  def opeing_price
    format_integer(@doc.search(@opening_price_path))
  end

  def high_price
    format_integer(@doc.search(@high_price_path))
  end

  def low_price
    format_integer(@doc.search(@low_price_path))
  end

  def closing_price
    format_integer(@doc.search(@closing_price_path))
  end

  def format_integer(nokogiri_format)
    nokogiri_format.inner_text.split.first.gsub(/(\d{0,3}),(\d{3})/, '\1\2')
  end

  def four_value_headers
    p @name
    p '始値 高値 安値 終値'
  end

  def four_value(topix = nil)
    if topix
      p "#{opeing_price} #{high_price} #{low_price} #{closing_price}"
    else
      p "#{opeing_price.to_i} #{high_price.to_i} #{low_price.to_i} #{closing_price.to_i}"
    end
  end
end
matsui_url = 'http://finance.matsui.co.jp/stocks/matsui/contents/futuresList.aspx'
sakimono_225 = FourValue.new(matsui_url, '225')

sakimono_225.opening_price_path = '/html/body/div[1]/main/div[2]/article/div[2]/div[2]/div[1]/div/div[2]/div/table/tbody/tr[1]/td'
sakimono_225.high_price_path = '/html/body/div[1]/main/div[2]/article/div[2]/div[2]/div[1]/div/div[2]/div/table/tbody/tr[2]/td'
sakimono_225.low_price_path = '/html/body/div[1]/main/div[2]/article/div[2]/div[2]/div[1]/div/div[2]/div/table/tbody/tr[3]/td'
sakimono_225.closing_price_path = '/html/body/div[1]/main/div[2]/article/div[2]/div[2]/div[1]/div/div[1]/div/table/tbody/tr[1]/td/b'

slack_url = 'https://hooks.slack.com/services/xxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxx'

slack = Slack::Incoming::Webhooks.new(slack_url)
slack.icon_emoji = ':yen:'
slack.post "#{sakimono_225.name}\n#{sakimono_225.four_value_headers}\n#{sakimono_225.four_value}"

sakimono_topix = FourValue.new(matsui_url, 'topix')

sakimono_topix.opening_price_path = '/html/body/div[1]/main/div[2]/article/div[2]/div[6]/div[1]/div/div[2]/div/table/tbody/tr[1]/td'
sakimono_topix.high_price_path = '/html/body/div[1]/main/div[2]/article/div[2]/div[6]/div[1]/div/div[2]/div/table/tbody/tr[2]/td'
sakimono_topix.low_price_path = '/html/body/div[1]/main/div[2]/article/div[2]/div[6]/div[1]/div/div[2]/div/table/tbody/tr[3]/td'
sakimono_topix.closing_price_path = '/html/body/div[1]/main/div[2]/article/div[2]/div[6]/div[1]/div/div[1]/div/table/tbody/tr[1]/td/b'

slack.post "#{sakimono_topix.name}\n#{sakimono_topix.four_value_headers}\n#{sakimono_topix.four_value(topix = true)}"
yahoo_url = 'https://info.finance.yahoo.co.jp/history/?code=7203'
toyota = FourValue.new(yahoo_url, 'トヨタ')
toyota.opening_price_path = "div#main tr:nth-child(2) > td:nth-child(2)"
toyota.high_price_path = "div#main tr:nth-child(2) > td:nth-child(3)"
toyota.low_price_path = "div#main tr:nth-child(2) > td:nth-child(4)"
toyota.closing_price_path = "div#main tr:nth-child(2) > td:nth-child(5)"
slack.post "#{toyota.name}\n#{toyota.four_value_headers}\n#{toyota.four_value}"

yahoo_url = 'https://stocks.finance.yahoo.co.jp/stocks/history/?code=7270.T'
subaru = FourValue.new(yahoo_url, 'SUBARU')
subaru.opening_price_path = "div#main tr:nth-child(2) > td:nth-child(2)"
subaru.high_price_path = "div#main tr:nth-child(2) > td:nth-child(3)"
subaru.low_price_path = "div#main tr:nth-child(2) > td:nth-child(4)"
subaru.closing_price_path = "div#main tr:nth-child(2) > td:nth-child(5)"
slack.post "#{subaru.name}\n#{subaru.four_value_headers}\n#{subaru.four_value}"