- 登入
- 註冊
我們在進行回測時,最先遇到的問題往往是:除權息的跳空該怎麼處理?不妥善處理的話,有偏差的回測結果會導致數據完全不具參考性。先前我們帶大家做過 ETF 配息歷史資料的爬蟲實作。這篇我們接著把上市和上櫃股票的除權息歷史爬蟲也搞定吧!
我們直接使用證交所與櫃買中心官方第一手數據,確保資料正確。換句話說,這隻爬蟲同樣也能爬到 ETF 配息歷史,不一定要用另一篇 MoneyDJ 網站作為數據來源。
上市證券除權息歷史,從網站進去的話是在「市場公告→計算結果表」中,如下圖。
上櫃證券除權息歷史,從網站進去的話是在「公告及法規→市場公告→除權除息→計算結果表」中,如下圖。
我們現在知道資料在什麼 URL 下了,那接下來各自破解吧!
上市的相對好爬,直接秒殺。首先,只需要使用 pandas。
import pandas as pd
接著,我們根據證交所的 URL 日期參數的規則,生成 URL 的字串,這樣能讓爬取不同日期區間的操作變得更方便。這裡的日期格式是八位數西元年月日,例如 “20220101”。本篇最後會整理呼叫函數的指令和全部程式碼。
在生成 URL 後,直接簡單暴力使用 pd.read_html 並取出爬取結果的第 0 號位置的 DataFrame,就大功告成了。
def get_twse_dividend_history(start_date: str, end_date: str):
url = f"https://www.twse.com.tw/exchangeReport/TWT49U?response=html&strDate={start_date}&endDate={end_date}"
dividend_history = pd.read_html(url)
return dividend_history[0]
上櫃證券相對麻煩一點,因為實際觀察瀏覽器的行為,發現呼叫回傳的結果,需要再稍微做點處理,才能解析成我們方便調用的格式。
因此,這裡不能直接用 pandas 爬蟲,只能乖乖用 requests 來爬,然後再用 pandas 處理欄位。
import requests
import pandas as pd
接下來我們列點解說:
def get_two_dividend_history(start_date: str, end_date: str):
# parse YYYYMMDD to YYY/MM/DD
start_date = f"{int(start_date[:4])-1911}/{start_date[4:6]}/{start_date[6:]}"
end_date = f"{int(end_date[:4])-1911}/{end_date[4:6]}/{end_date[6:]}"
url = f"https://www.tpex.org.tw/web/stock/exright/dailyquo/exDailyQ_result.php?l=zh-tw&d={start_date}&ed={end_date}"
dividend_history_columns = [
"除權息日期",
"代號",
"名稱",
"除權息前收盤價",
"除權息參考價",
"權值",
"息值",
"權值+息值",
"權/息",
"漲停價",
"跌停價",
"開始交易基準價",
"減除股利參考價",
"現金股利",
"每仟股無償配股",
"現金增資股數",
"現金增資認購價",
"公開承銷股數",
"員工認購股數",
"原股東認購股數",
"按持股比例仟股認購",
]
res = requests.get(url)
dividend_history = pd.DataFrame(
res.json()["aaData"], columns=dividend_history_columns
)
return dividend_history
接著我們來呼叫這兩個函數吧!假設我們指定的是 2022 全年的除權息歷史資料,呼叫方式如下。
start_date = “20220101”
end_date = “20221231”
twse_dividend_history = get_twse_dividend_history(start_date, end_date)
twse_dividend_history.to_csv("test.csv", index=False)
two_dividend_history = get_two_dividend_history(start_date, end_date)
two_dividend_history.to_csv("test_2.csv", index=False)
如此一來爬蟲就完成了!不過,實際上我們在使用時,往往還會追求欄位名稱統一。這部分沒什麼難度,就依各位讀者的喜好自行處理囉!