質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.35%

【Ruby】クローラーでネットから抜き出した情報が保存されない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,004

Yc_Kz

score 13

前提・実現したいこと

Ruby初心者ですが、
Rubyを使用して、ネットから株価情報を抜き出すシステムを作成しようとしております。
上場市場と売買株数を抜き出し、txtファイルとして保存したいです。

発生している問題・エラーメッセージ

txtファイルは作成されますが、中身は空のままです。
また、エラーメッセージなどは表示されておりません。

該当のソースコード

# coding: Windows-31J

require "open-uri"

# 日経の銘柄情報を取得し、銘柄リストを作るクラス
# 証券コード、上場市場、単元株数・売買単位が含まれる

class StockListMaker

  attr_accessor :data_dir, :file_name


  def initialize(market)

    @market = market
    @data_dir = "data"
    @stock_info = []
  end


  # 銘柄情報の取得

  def get_stock_info(code)
  page = open_page(code)
    return unless page
   text = page.read.encode("Windows-31J" , :undef => :replace)

    data = parse(text)
    data[:code] = code
      return unless data[:matket_section]
        puts code
        @stock_info << data
  end


  # 銘柄情報の保存

  def save_stock_list
    File.open(@data_dir + "/" + @file_name, "w") do |file|
      @stock_info.each do |data|
        file.puts [data[:code], data[:market_section], data[:unit]].join(",")
      end
    end
  end


 private

  # 銘柄情報ページを開く

 def open_page(code)
   begin
     open("http://www.nikkei.com" + "/nkd/company/?scode=#{code}")
   rescue OpenURI::HTTPError
     return
   end
 end


  # HTMLから銘柄情報を抜き出す

def parse(text)
  data = Hash.new

  sections = []
    reg_market = /option_mk" >([^< ]+) ?</
    reg_unit = %r!<span class="m-stockInfo_detail_value">(\d|,)<span class="m-stockInfo_detail_unit"> 株</span>!

    text.lines do |line|
      if line = reg_market
        sections << $+
      elsif line =~ reg_unit
        data[:market_section] = sections[0]
          data[:unit] = get_unit($+)
            return data
      end
    end
    data
end



# 単元株数を得る

def get_unit(str)
  if str == "---"
    "1"
  else
    str.gsub(/,/,"")
   end
  end
end
# codeing: Windows-31J

require "B:/trade_simulator/lib/stocklistmaker"


# 東証銘柄リストを作る

slm = StockListMaker.new(:t)

slm.file_name = ARGV[0] || "tosho_list.txt"

puts slm.file_name

(1400..1402).each do |code|

 slm.get_stock_info(code)

end

slm.save_stock_list

試したこと

parseメソッドのif end後(下から2つ目のendと3つ目のendの間)で
puts dataを実行すると、

{}
{}
{}
{}
{}
{}
{}


上記のような実行結果が返されます。
このことより、読み込んだデータを保管できていないのかと思っております。

エンコードのせいかと思い、coding:UTF-8でも試しましたが、ダメでした。
正規表現も見直しましたが、わかりませんでした。

補足情報(言語/FW/ツール等のバージョンなど)

・Ruby 2.3.3
・Eclipseプラットフォーム 4.6.3

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

# encoding: utf-8

require'open-uri'
require'certified'

def test(code)
  data = open("http://www.nikkei.com/nkd/company/?scode=#{code}", &:read)

  stock = data.scan(/<span class="m-stockInfo_detail_value\s*">(.+?)<span class="m-stockInfo_detail_unit"> 株/).flatten.first
  market = data.scan(/<span class="m-company_data_select_btn .+?" data-tab=".m-company_data_option_mk" >(.+?)<\/span>/).flatten.first

  [stock, market]
end

# =================================
# open('market.txt', 'w') do |f|
#   f.puts(test('3197').join("\s"))
# end
# =================================

puts test('3197').join("\s") # => 10,152,600 東証1部


考えてみました。

補足

使い方
3193~3197の「上場市場と売買高」を取得してsample.txtに書き込む場合。

# encoding: utf-8

require'open-uri'

def test(code)
  data = open("http://www.nikkei.com/nkd/company/?scode=#{code}", &:read)

  market = data.scan(/<span class="m-company_data_select_btn .+?" data-tab=".m-company_data_option_mk" >(.+?)<\/span>/).flatten.first
  stock = data.scan(/<span class="m-stockInfo_detail_value\s*">(.+?)<span class="m-stockInfo_detail_unit"> 株/).flatten.first

  [market, stock]
end

(3193..3197).each do |code|
  open('sample.txt', 'a') do |f|
    f.puts(test(code).join("\s"))
  end
end


企業名と業界を追加したもの

# encoding: utf-8

require'open-uri'

def test(code)
  data = open("http://www.nikkei.com/nkd/company/?scode=#{code}", &:read)

  title, industry = data.scan(/<meta property="og:title" content="(.+?)"/).flatten[0].split(':').shift(2)
  market = data.scan(/<span class="m-company_data_select_btn .+?" data-tab=".m-company_data_option_mk" >(.+?)<\/span>/).flatten.first
  stock = data.scan(/<span class="m-stockInfo_detail_value\s*">(.+?)<span class="m-stockInfo_detail_unit"> 株/).flatten.first

  [title, industry, market, stock]
end

(3193..3197).each do |code|
  open('sample.txt', 'a') do |f|
    f.puts(test(code).join("\t"))
  end
end


実行結果例

鳥貴族[3193]      飲食店     東証1部    221,100
キリン堂ホールディングス[3194]      専門店・ドラッグストア     東証1部    51,600
ジェネレーションパス[3195]      インターネットサイト運営     マザーズ    22,100
ホットランド[3196]      弁当・デリバリー     東証1部    47,000
すかいらーく[3197]      飲食店     東証1部    10,152,600

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/18 18:01

    h_a様

    回答ありがとうございます。
    頂いたコードを既存コードに追加し、

    ```ruby
    # encoding: utf-8

    require'open-uri'
    require'certified'

    中略

    def open_page(code)
    begin
    data = open("http://www.nikkei.com/nkd/company/?scode=#{code}", &:read)
    rescue OpenURI::HTTPError
    return
    end
    end

    def parse(text)
    data = Hash.new
    sections = []
    reg_market = data.scan(/<span class="m-stockInfo_detail_value\s*">(.+?)<span class="m-stockInfo_detail_unit"> 株/).flatten.first
    reg_unit = data.scan(/<span class="m-company_data_select_btn .+?" data-tab=".m-company_data_option_mk" >(.+?)<\/span>/).flatten.first
    text.lines do |line|
    if line = reg_market
    sections << $+
    elsif line =~ reg_unit
    data[:market_section] = sections[0]
    data[:unit] = get_unit($+)
    return data
    end
    end
    data
    end

    後略

    ```

    と、してみましたが、

    `require': cannot load such file -- certified (LoadError) といった形でrequireが上手く作動致しませんでした。
    require'certified'をコメントアウトして再度実行したところ、 `get_stock_info': undefined method `read' for #<String:0x00000002274f00> (NoMethodError)と、何故かget_stock_infoメソッドのreadメソッドがエラーになりました。

    調べてもよく分からなく、Rubyの最新バージョンに合う良い本も見つからず、せっかくお答え頂いたのに活用出来なく、大変申し訳ありません。
    どうぞ、よろしくおねがいいたします。

    キャンセル

  • 2017/06/18 19:13 編集

    certifiedはopenでエラーが発生する人が使うライブラリなので
    なくても大丈夫なら無視していただいて構いません。
    後からインストールするものなので何もしていなければ存在しません。
    私のコードの使い方を追記しました。

    キャンセル

  • 2017/06/20 06:56

    h_a様

    回答ありがとうございます。
    h_a様のコードを実行してみましたが、invalid multibyte char (utf-8)とエラーが出たので調べてみたら、作成しているRubyファイルの文字コードがUTF-8ではありませんでした。
    UTF-8として上書き保存し、実行した所、正常にsample.txtが作成され、情報も記載されておりました。

    以上、ベストアンサーとさせて頂きます。
    細かくお教えいただき、誠にありがとうございました。

    キャンセル

  • 2017/06/20 08:13

    いえいえ。どういたしまして。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る