我們在進行回測時,最先遇到的問題往往是:除權息的跳空該怎麼處理?不妥善處理的話,有偏差的回測結果會導致數據完全不具參考性。因此,為求回測結果準確,我們需要標的配息時間和股息金額的歷史資料。
本篇接下來就一步一步帶大家完成 ETF 配息歷史資料爬蟲的實作吧!
ETF 配息歷史資料來源
首先,我們使用查詢 ETF 相關資料常用的網站 MoneyDJ,在每檔 ETF 資訊的頁面中,其中有一個頁籤叫做「配息紀錄」,其中就包含了我們需要的配息歷史表格!如下方截圖,或是從下方連結進入 0050 (台灣 50 ETF)的配息紀錄頁面。

台灣 50 ETF的配息紀錄頁面:https://www.moneydj.com/ETF/X/Basic/Basic0005.xdjhtm?etfid=0050.TW
注意網址的命名規則
在切換幾檔 ETF 之後,我們發現不同 ETF 的網址只差在 etfid 後的值。這樣的話一切都很單純了!代表我們可以建立一個函數,只要傳入不同的 ETF股票編碼,就能爬取到它的配息歷史數據了!
爬蟲實作-ETF 配息歷史
我們現在有網址了,那接下來解說一下爬取方式吧!
首先,我們 import 爬蟲及資料整理的常用套件。相關套件的基礎操作教學請詳見量化通其他文章。
import requests
import numpy as np
import pandas as pd from bs4
import BeautifulSoup
接著,為了方便重複使用爬蟲程式碼,如先前所述,我們確定了網址的命名規則。因此,下面我們展示設計完成的函數直接進行解說:
def get_dividend_list(symbol):
div_url = f'https://www.moneydj.com/ETF/X/Basic/Basic0005.xdjhtm?etfid={symbol}.TW'
r = requests.get(div_url)
soup = BeautifulSoup(r.text, "lxml")
table = soup.findAll("table", class_ = "datalist")[0]
list_rows = []
rows = table.find_all('tr')
for row in rows:
row_td = [i.text for i in row.find_all('td')]
if len(row_td)>1:
list_rows.append(np.array(row_td)[[1,2,6]])
df = pd.DataFrame(list_rows, columns =
['ex_div_date','pay_date','div_amount'] )
df['symbol_id'] = [symbol] * len(df)
return df
- 函數的傳入參數 symbol 為字串,例如 ”006208” 等。
- 在傳入參數後,我們把代碼放入 URL 中。這裡使用的是 Python 3.6 之後新增的字串格式寫法:雙引號左側放一個 f,中間的變數用大括號 {} 包起來。
- 接著我們用 requests 套件的 get 方法把 URL 的訪問內容拿回來,如函數內第二行程式碼。
- 再來,用 BeautifulSoup 解析網頁內容,並且搜尋第一個 class_ 值為 “datalist” 的table(知道要這麼解析,是因為我們已經事先把訪問內容列印出來,知道網頁結構是如此)
- 進入 for 迴圈即是典型的表格解析方法,先以 te 區分每一列,再以 td 區分每一個儲存格。在這個範例中,我們僅需要用到第 1, 2, 6 行,因此在儲存每一列資料到 list_rows 時,我們刻意只取 [1, 2, 6]。
- 最後,我們把解析完的資料製作成 pd.DataFrame,另外再加入一個 column 來註記這是哪個 ETF 的配息歷史資料,就大功告成啦!
為什麼要另外製作 ETF 代碼的 colomn 呢?試想一下,如果我們現在有 200 個 ETF 的配息歷史資料需要爬取,搭配 ETF 代碼的 colomn 後,我們就能夠把所有資料整併進一個 pd.DataFrame ,方便管理和後續取用。
以 0050 為例,我們試著運行看看這個爬蟲程式:
dividend = get_dividend_list(“0050”)
print(dividend)
量化通粉絲社群,一起討論程式交易!