爬蟲第八步:用 Python 爬蟲取得股票除權息歷史

一、前言

我們在進行回測時,最先遇到的問題往往是:除權息的跳空該怎麼處理?不妥善處理的話,有偏差的回測結果會導致數據完全不具參考性。先前我們帶大家做過 ETF 配息歷史資料的爬蟲實作。這篇我們接著把上市和上櫃股票的除權息歷史爬蟲也搞定吧!

立即訂閱電子報,掌握最新資訊!

    電子郵件

    有興趣的主題
    量化交易金融知識台灣股市國內期貨海外期貨虛擬貨幣

    有興趣的量化交易軟體/平台
    不清楚MultiChartsTradingViewPythonXQMT4MT5

    還有什麼詢問的?

    【早鳥報名中】元富證券 X 量化通 模擬量化交易競賽

    好富投 1920x400
    好富投 978x258

    點我了解更多資訊


    二、除權息歷史資料來源

    我們直接使用證交所與櫃買中心官方第一手數據,確保資料正確。換句話說,這隻爬蟲同樣也能爬到 ETF 配息歷史,不一定要用另一篇 MoneyDJ 網站作為數據來源。

    上市證券除權息歷史,從網站進去的話是在「市場公告→計算結果表」中,如下圖。

    Python Stock Ex Dividend 101202131514

    上櫃證券除權息歷史,從網站進去的話是在「公告及法規→市場公告→除權除息→計算結果表」中,如下圖。

    Python Stock Ex Dividend 101202131515

    我們現在知道資料在什麼 URL 下了,那接下來各自破解吧!

    Py 101209161710
    Py 101209161711

    三、上市證券爬蟲

    上市的相對好爬,直接秒殺。首先,只需要使用 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

    接下來我們列點解說:

    • 櫃買中心 URL 所接受的日期格式是 “民國年/月/日”,但我們設計成函數就是為了更一般化使用,所以我們傳入 start_date 和 end_date 一樣是傳入八位數的西元年月日字串,但是多了一道日期格式轉換的手續。如下方程式碼第 2 ~ 4 行。
    • 生成日期後,即可生成爬蟲的 URL。如第 5 行。
    • 根據櫃買中心的網站欄位名稱,我們手動定義一個 list 作為之後使用。因為櫃買中心的 URL 並不會回傳欄位名稱。如下方很長的一大串中文字,這個換行規則是使用 VSCode 內 formatter 做程式碼格式化,以便後續維護程式的閱讀。
    • 使用 requests.get 獲取資料,並用 .json() 進行解析,取出其中 “aaData” 下對應的資料。這些資料即可直接使用 pd.DataFrame 做成大家熟悉的 DataFrame。剛才製作的欄位名稱也可以順手放進來。這樣就大功告成了!
    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)

    如此一來爬蟲就完成了!不過,實際上我們在使用時,往往還會追求欄位名稱統一。這部分沒什麼難度,就依各位讀者的喜好自行處理囉!

      電子郵件

      有興趣的主題
      量化交易金融知識台灣股市國內期貨海外期貨虛擬貨幣

      有興趣的量化交易軟體/平台
      不清楚MultiChartsTradingViewPythonXQMT4MT5

      還有什麼詢問的?


      量化通粉絲社群,一起討論程式交易!

      加入LINE社群量化交易討論群」無壓力討論與分享!

      加入臉書社團「程式交易 Taiwan」即時獲取實用的資源!

      QP66
      QP66

      具備逾十年交易經驗,研究交易資產橫跨股票、債券、外匯、原物料,以及加密貨幣。現為量化避險基金交易員,亦曾任職於資金規模逾百億的避險基金,以及在區塊鏈企業擔任顧問一職。

      擅長從宏觀至微觀,由淺入深挖掘交易機會,並運用Python實現全自動化的投資組合管理。

      文章: 24

      發佈留言

      發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *