Python爬蟲:爬取證交所的發行量加權股價指數歷史資料

簡單使用pandas整理並會成csv檔跟json檔

MinKuan
8 min readDec 12, 2020

前言

今天將會分享如何使用python爬取證交所的近10年的發行量加權股價指數歷史資料,本篇將會著重於重複抓取以及匯成.csv跟.json。

在開始爬蟲時都要先研究 目標是誰?結構是甚麼?我要什麼資料?希望最後的資料長怎麼樣? 在腦中有些許畫面後,我們就可以開始了!!!

將學習到

· 使用pandas的read_html爬取表格並整理

· 利用pandas的to_csv匯成.csv檔跟to_json匯成.json

環境設定

win10 Anaconda的jupyter(6.0.3)

使用套件

import requests
import bs4
import pandas as pd
import time#時間套件
import random#亂數套件

開始工作

步驟1:檢查目標

https://www.twse.com.tw/zh/page/trading/indices/MI_5MINS_HIST.html
https://www.twse.com.tw/indicesReport/MI_5MINS_HIST?response=html&date=20201211

看到目標網站(上圖)中並點擊列印/HTML,即可以看到真正的目標表格(下圖),再看到連結的尾巴有’20201211',這就是重複爬蟲抓取的重點,可以利用手動更改這串日期去找相對的歷史資料。

可以先點開該網站並按滑鼠右鍵”檢查”,先快速看過此網站結構。

步驟2:先爬取一次並看成果

先用request請求網頁再用BeautifulSoup解析html,接著用pandas轉成DataFrame和整理資料。

#請求網站
url=”https://www.twse.com.tw/indicesReport/MI_5MINS_HIST?response=html&date=20100601"
res = requests.get(url)
#解析網站
soup = bs4.BeautifulSoup(res.text,”html.parser”)
#取得table並整理
data = soup.select(‘table’)[0]#找到table
df = pd.read_html(data.prettify())[0]#將data整理並將第1個建立成DataFrame
df.columns=df.columns.get_level_values(1)#取得第2欄的標頭
df.columns=[u’date’,u’open’,u’high’,u’low’,u’close’]#將標頭重新命名
df.head(5)#顯示前5筆資料
抓取第1次的整理成果

步驟3:多次爬取歷史資料並會匯成.csv跟.json

  • 重複抓取利用更改網址中尾巴的日期,而因為每個月一定都有1號,所以依照西元年分和月份抓取即可。
  • month_list是因為月份有單位數1月雙位數12月,在抓取發行量加權股價指數歷史資料的1月份要輸入01不然很容易會被網頁判別為10月。
  • 中間利用time.sleep()去防止被證交所鎖住,而為了不像機器人所以用了time.sleep(random.uniform(0.1,2.1)),也就是亂數休眠去讓網站更難判別是人為操作還是機器人。
  • 為了要合成DataFrame所以用到了pd.concat(),將每次的df加入到一個大dfs。DataFrame合併有pd.concat()df.append()pd.merge()df.join(),關於如何使用DataFrame合併可以看最下方的參考資料。
  • 由於證交所給的資料是民國年,但在多數的使用情形都是用西元年,所以我這邊用了較為土炮的方式,將每一格date拆解並將前面年份改改為西元年再合併回去。
  • 要使用to_jsonto_csv前提為需要是DataFrame。而裡面的參數可以依照你的路徑和需求做選擇。to_json裡的orient=”records”是依照你需要的json檔形式去做調整,orient裡面可以選項項目有split、records、index、values、table、columns (the default format),關於orient的呈現形式差別可以看最下方的參考資料。而to_csv裡的index=False是指不將index的流水號寫進.csv檔裡面。
#多次爬蟲
import requests
import bs4
import pandas as pd
import time
import random
dfs = pd.DataFrame(columns=['date','open','high','low','close'])#建立DataFrame最後則是拿這個做使用month_list=['01','02','03','04','05','06','07','08','09','10','11','12']#跑月份字串
for i in range(2010,2021,1):
for j in range(0,12,1):

#請求網站
url="https://www.twse.com.tw/indicesReport/MI_5MINS_HIST?response=html&date="+str(i)+month_list[j]+"01"
print(url)
res = requests.get(url)
time.sleep(random.uniform(0.1,2.1))
#解析網站
soup = bs4.BeautifulSoup(res.text,"html.parser")

#取得table並整理
data = soup.select('table')[0]
df = pd.read_html(data.prettify())[0]
df.columns=df.columns.get_level_values(1)
df.columns=[u'date',u'open',u'high',u'low',u'close']

#合併DataFrame到dfs
dfs = pd.concat([dfs,df],ignore_index=True)

#使用隨機間隔時間抓取網站,避免被鎖
time.sleep(random.uniform(1.1,5.5))

print('finish dfs')
#將調整民國年調整成西元年
for i in range(len(dfs.date)):
sp = dfs.date[i].split('/')
dfs.date[i]=str(int(sp[0])+1911) + '/' + sp[1] + '/' + sp[2]

print('finish date')
#建立資料庫.json
dfs.to_json("C:/user/user/desktop/history.json",orient="records")
#建立資料庫.csv
dfs.to_csv("C:/user/user/desktop/history.csv",index=False)
print('finish to_json & to_csv')

成果展示

history.json展示
history.csv展示

完整程式碼

小結

這次沒有將每一步都分成一個區塊寫是因為我認為一次寫完我的思考邏輯和一次秀出程式碼會較容易理解,如果你對於以上的內容有建議歡迎提出,一起討論絕對是成長的捷徑!!

參考資料

Joining DataFrames in Pandas

How to Export Pandas DataFrame to JSON File

Pandas 105:更多進階資料框技巧

--

--

MinKuan
MinKuan

No responses yet