iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0
自我挑戰組

Ruby on Rails 與它們相關的東西 II系列 第 20

Day20 - 用 Ruby on Rails 抓臺灣證券交易所資料-每日收盤行情

前言

這篇開始會有幾篇是與「臺灣證券交易所」有關,示範如何用 Ruby on Rails 來爬蟲將資料抓回來處理,並自己建立 DB,方便自己在 Local 可以測試

這部分起,不提供 sample pr or repo (個人有放 GitHub private,目前沒打算公開)

預計會示範如何抓「每日收盤行情」、「除權除息計算結果表」、設計 DB 架構、寫點簡易的技術指標選股

謎之音: 其實是原本規劃的題目,寫到膩了,想調整下內容 (才不承認是拿之前自己做的 Side Project 來擠牙膏)

說明

這邊不會教股票相關的知識,我也只是一隻小菜鳥,會以技術實作層面呈現,若有寫錯或更好的寫法,歡迎不吝指教

取得「每日收盤行情」CSV 檔

目標是從臺灣證券交易所的「每日收盤行情」取得每日的 CSV 檔

note: 本資訊自民國93年2月11日起提供

下載的檔案內容如下

從上面已經知道,只提供 2004-02-11 之後的檔案

實作

預計會用到以下 3 個 Gem,分別是 iconvdownactiverecord-import

note: Mac 的話,iconv 可以不用裝,最初是在 Windows 上寫的,後來變成在兩種作業系統輪流寫 XD

# Gemfile

gem 'iconv', '~> 1.0', '>= 1.0.8'
gem 'down', '~> 5.2', '>= 5.2.2'
gem 'activerecord-import', '~> 1.1'

由於已經知道下載、儲存 DB 會有許多方法是共用的,因此這邊直接抽 helpers

note: 剛開始做的時候不知道,邊做邊重構,慢慢整理的,其中 decode_data 這方法會在存 DB 時才會用到,因此可先忽略

# app/features/twse/helpers.rb

module Twse
  module Helpers

    BASE_URL = "https://www.twse.com.tw/".freeze

    private

    # ----- download start -----
    def upload_to_github
      need_update_github = `cd data/twse && git remote -v`

      system('cd data/twse && git add . && git commit -m "update data" && git push') if need_update_github.present?
    end
    # ----- download end -----

    # ----- save_to_db start -----
    def decode_data(file_path, is_linux)
      if is_linux
        # for windows Ubuntu
        file = File.open(file_path, "r:UTF-8")
        decoded_data = Iconv.iconv("utf-8", "big-5", file.read)
        decoded_data[0].split("\r\n")
      else
        # for mac
        file = File.open(file_path, "r:BIG5")
        decoded_data = file.read
        decoded_data.split("\r\n")
      end
    end
    # ----- save_to_db end -----

  end
end

下載每日收盤行情中的「每日收盤行情(全部(不含權證、牛熊證、可展延牛熊證))」

note: 不能太密集的抓資料,會被 Bang,至於間隔幾秒怎麼知道的,trial and error 是一個方法

# app/features/twse/allbut_0999/download.rb

module Twse::Allbut0999
  class Download

    include Twse::Helpers

    def execute
      current_time = Time.current
      puts "#{self.class}, start_time: #{current_time.to_s}"
      data_path = Rails.root.join("data/twse/ALLBUT0999")
      start_date = find_latest_transaction_date(current_time)
      return puts "#{self.class}, 已經是最新的資料" if start_date == false

      (start_date..Date.current).each do |date|
        month_path = data_path.join(date.year.to_s, date.month.to_s)
        file_path = month_path.join("MI_INDEX_ALLBUT0999_#{date.strftime("%Y%m%d")}.csv")
        next if File.exists?(file_path) || date.sunday?

        FileUtils.mkdir_p(month_path) unless File.directory?(month_path)

        download_file(date, month_path)
        sleep 3 # 太密集抓資料會被 bang
      end
      upload_to_github
      puts "#{self.class}, done_time:#{Time.current}, #{(Time.current - current_time).to_s} sec"
    rescue StandardError => e
      puts "errors: #{e.inspect}, backtrace: #{e.backtrace}"
    end

    private

    def find_latest_transaction_date(current_time)
      latest_transaction_date = DailyQuote.latest_transaction_date
      if latest_transaction_date
        return false if latest_transaction_date == current_time

        latest_transaction_date + 1.day
      else
        Date.parse("2004-02-11") # 僅支援抓 2014-02-11 之後的資料
      end
    end

    def download_file(date, month_path)
      remote_file = Down.download("#{BASE_URL}exchangeReport/MI_INDEX?response=csv&date=#{date.strftime("%Y%m%d")}&type=ALLBUT0999")

      if remote_file.size.zero?
        FileUtils.rm_rf(remote_file.path)
      else
        FileUtils.mv(remote_file.path, month_path.join(remote_file.original_filename))
      end
    end

  end
end

小結

製作的過程,踩到蠻多雷的 (ex: 下載要間隔幾秒、如何處理資料...等),可能與一開始在 Windows 上開發有關,逐一解決遇到的問題,還蠻好玩的,主要是享受開發的過程

上面有寫 upload_to_github 這方法,將抓下來的資料,自動上傳到 GitHub repo,若只是想單純試看看,沒有要自己建 repo 的話,這段可移除

find_latest_transaction_date 中的 DailyQuote 為建立的 Model,這篇可以先略過,後面會有一篇文章說明 DB 的設計

另外也可以到「臺灣證券交易所 OpenAPI」打 API 取資料,這邊就不多闡述了

下一篇會示範如何抓「除權除息計算結果表」的資料


鐵人賽文章連結:https://ithelp.ithome.com.tw/articles/10272778
medium 文章連結:https://link.medium.com/DtL2qRIuTjb
本文同步發布於 小菜的 Blog https://riverye.com/

備註:之後文章修改更新,以個人部落格為主


上一篇
Day19 - 匯入 excel-測試篇
下一篇
Day21 - 用 Ruby on Rails 抓臺灣證券交易所資料-除權除息計算結果表
系列文
Ruby on Rails 與它們相關的東西 II30

尚未有邦友留言

立即登入留言