網頁

2019年5月28日 星期二

程式語言: 用 Python 下載歷史股價 - 依交易日列出當日所有股票之股價

創用 CC 授權條款
南台灣。木星人MHYen製作,以創用CC 姓名標示-非商業性-禁止改作 3.0 台灣 授權條款釋出。
從網頁下載的原始資料
很久以前木星人有寫一篇用 R 抓取歷史股價的文章,後來發現 R 的套件是透過 yahoo 或是 google finance 網頁抓取台股資料會有缺漏、不連續的問題,沒辦法取得台股 10 年的資料!
因此第一個要問的問題是:台股最完整的資訊在哪裡呢?答案是台灣自己的資料庫網站 - 台灣的證券交易所
第二個問題是:要怎麼取得台股 10 年每日的收盤行情? 已經很確定 R 會被限制在內建的套件程式,因此網路上查找的結果是 Python 是很好學習且可網路攫取的程式。除此之外對於載下的資料還可做資料處理及分析。於是木星人開始了 Python 的日子!

當初選擇 Python 還有一個很大的原因,就是超多人在用 Python 的,所以有任何問題上網查找大約都可以找到程式範例或相關的解決方案,甚至國外有很大的論壇專門在討論或是協助解決大家的疑難雜症...,總之 Python 高手雲集呀!而木星人從中得到許多厲害的人的無私奉獻,所以當然要把這些眾人智慧的結晶整理出來,這才是 Open Source 的精神!

木星人抓取股價檔的想法是以交易日為主,取得交易日當日所有的上市公司的股價,進行監控的網站是證交所的每日收盤行情


從網頁中可以看到幾個搜尋的選項,而我們要監控網頁的目的就是要找到主要的 Request URL 就是這段文字:http://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&date=20170928&type=ALLBUT0999
我們可以知道這是一段 jason 格式的 Request,以及接下來要輸入的變數值 date=20170928。由此可知:
第一,我們使用 BeautifulSoup 的時候需要 import jason。
第二,只要建立一個日期的表單套用在 Request URL 中的 date=xxxxxxxx 就可以讓程式攫取到當日所有公司的股價資料。

如何監控網頁並取得 Request URL 可參考以下影片教學:https://youtu.be/IqrFMiJfHBU。木星人的程式就是以此作者分享的 Source Code 進行修改,他是以公司代碼及日期進行網路資料攫取的。

話不多說先把程式整體秀出來:
# -*- coding: utf-8 -*-

from bs4 import BeautifulSoup as bs
import requests
import json
import csv
import time, datetime, os

#make the date formate the same with the url formate 
def get_dates(inf):
    for row in csv.DictReader(inf):
        year, month, day = row['日期'].split('/')
        yield '{}{:02}{:02}'.format(int(year) + 1911, int(month), int(day))

#Collect into a list
with open('./yourdatelist.csv') as inf:
    date_list = list(get_dates(inf))

# ###http://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&date=20170928&type=ALLBUT0999
#standard web crawing process
def get_webmsg(date):
    time = str(date)  ##format is yyyymmdd
    url_twse = 'http://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&date='+time+'&type=ALLBUT0999'
    res = requests.post(url_twse,)
    soup =  bs(res.text, 'html.parser')
    smt = json.loads(soup.text)    #convert data into json
    return smt

def write_csv(date, directory, filename, smt):
    writefile = directory + filename        #set output file name
    outputFile = open(writefile, 'w', newline='')
    outputWriter = csv.writer(outputFile)
    outputWriter.writerow(smt['fields4'])
    for data in (smt['data4']):
        outputWriter.writerow(data)

    outputFile.close()

#create a directory in current one doesn't exist
def makedirs(date):
    yyy = str(date)
    directory = './'
    if not os.path.isdir(directory):
        os.makedirs(directory)       #os.makedirs able to create multi folders


for date in date_list:
#    for month in month_list:
#        if (dt.year == year and month > dt.month): break
    yyy = str(date)
    directory = './' #setting directory
    filename = str(yyy)+'.csv'  #setting file name
    smt = get_webmsg(date)     #put the data into smt
    makedirs(date)                    #create directory funtion
    write_csv(date, directory, filename, smt) #write files into CSV
    time.sleep(2)


首先先說明網路攫取的主程式:
我們將日期這變數自動輸入 Request URL 就可以自動連結到資料網頁,就不必一次一次用手去點選查照,因此主程式第一個函式 :def get_webmsg(date) 這段如下所示,以日期為變數一次次帶入並連結到 requests.post,並且將資料轉為 json 的格式。
# ###http://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&date=20170928&type=ALLBUT0999
#standard web crawing process
def get_webmsg(date):
    time = str(date)  ##format is yyyymmdd
    url_twse = 'http://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&date='+time+'&type=ALLBUT0999'
    res = requests.post(url_twse,)
    soup =  bs(res.text, 'html.parser')
    smt = json.loads(soup.text)    #convert data into json
    return smt


主程式第二個函式 :def write_csv(date, directory, filename, smt) 這段如下所示,主要就是將request到的資料下載到自己的電腦中。這裡是寫成 csv 檔的格式。
def write_csv(date, directory, filename, smt):
    writefile = directory + filename        #set output file name
    outputFile = open(writefile, 'w', newline='')
    outputWriter = csv.writer(outputFile)
    outputWriter.writerow(smt['fields4'])
    for data in (smt['data4']):
        outputWriter.writerow(data)

    outputFile.close()

程式最主要要的部份已經交待完了,接下來分別說明整體程式的前後部份:

首先是前置作業,程式如下:
#make the date formate the same with the url formate 
def get_dates(inf):
    for row in csv.DictReader(inf):
        year, month, day = row['日期'].split('/')
        yield '{}{:02}{:02}'.format(int(year) + 1911, int(month), int(day))

#Collect into a list
with open('./yourdatelist.csv') as inf:
    date_list = list(get_dates(inf))
就是將日期列表轉為 URL 需要的格式。而日期列表是木星人之前已經建立好的表單,而這部份其實也可以用同樣的網路攫取方式到證交所攫取正確的股市交易日。

主要函式後面的程式,是建立一個檔案路徑的函式,可將抓取下來 csv 檔自動存入這個檔案路徑
#create a directory in current one doesn't exist
def makedirs(date):
    yyy = str(date)
    directory = './'
    if not os.path.isdir(directory):
        os.makedirs(directory)       #os.makedirs able to create multi folders


最後是啟動之前建立的函式,讓他們幫你工作!
for date in date_list:
#    for month in month_list:
#        if (dt.year == year and month > dt.month): break
    yyy = str(date)
    directory = './' #setting directory
    filename = str(yyy)+'.csv'  #setting file name
    smt = get_webmsg(date)     #put the data into smt
    makedirs(date)                    #create directory funtion
    write_csv(date, directory, filename, smt) #write files into CSV
    time.sleep(2)
其中 time.sleep() 是在每次攫取時稍微 delay 幾秒,這樣才不會癱瘓整個網路,木星人有幾次在交易日的上班時間交易正熱絡,由於 delay 的時間設太短而被斷線過,印象中大約半天後才可回覆。要不然通常下載速度滿快的!


參考資料:
https://youtu.be/IqrFMiJfHBU
https://youtu.be/kurBNu1qobM


延伸閱讀:
程式語言 : 用 R 理財, 下載歷史股價合併至財務報表

沒有留言:

張貼留言