这篇开始会有几篇是与「台湾证券交易所」有关,示范如何用 Ruby on Rails 来爬虫将资料抓回来处理,并自己建立 DB,方便自己在 Local 可以测试
这部分起,不提供 sample pr or repo (个人有放 GitHub private,目前没打算公开)
预计会示范如何抓「每日收盘行情」、「除权除息计算结果表」、设计 DB 架构、写点简易的技术指标选股
谜之音: 其实是原本规划的题目,写到腻了,想调整下内容 (才不承认是拿之前自己做的 Side Project 来挤牙膏)
这边不会教股票相关的知识,我也只是一只小菜鸟,会以技术实作层面呈现,若有写错或更好的写法,欢迎不吝指教
目标是从台湾证券交易所的「每日收盘行情」取得每日的 CSV 档
note: 本资讯自民国93年2月11日起提供
从上面已经知道,只提供 2004-02-11 之後的档案
预计会用到以下 3 个 Gem,分别是 iconv、down、activerecord-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] Flutter with GetX something else
今天的实作内容主要根据教学网站进行。 Django提供了身份认证与授权系统,不论是登入、登出、密码管...
是说TS针对型别的类型也太讲究,写好多天还没写完(其实是我30篇不够XDD),哈哈不罗嗦, 今天继...
身为开发时程紧凑的工程师 遇到问题或是疑惑时必须要能快速的排除 通常在专案中遇到不熟悉的物件,想到 ...
前言 写CSS还蛮常会用到阴影,使元素更具有立体感,今天就来认识一下阴影有什麽属性吧。 首先我们要先...
平均数的信赖区间 若想知道一个班级学期成绩的90%信赖区间 首先建立学生学期成绩的资料 点选工具表中...