iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 8
2
Software Development

Python 程式交易 30 天新手入門系列 第 8

Day-08 資料蒐集:取得個股每月各交易日盤後資訊 + 解析 CSV

解析 CSV

以取得個股每月各交易日盤後資訊為例

分析資料結構

https://www.twse.com.tw/exchangeReport/STOCK_DAY?response=csv&date=20190401&stockNo=1101
個股每日成交資訊

  1. 第 1 行為標題
  2. 第 2 行是標頭行
  3. 最後 5 行是附註資訊及空行

定義儲存類別

class AfterHoursDailyInfo:
    def __init__(
        self,
        code,
        date,
        totalShare,
        totalTurnover,
        openPrice,
        highestPrice,
        lowestPrice,
        closePrice):
        # 代碼
        self.Code = code
        # 日期
        self.Date = date
        # 成交股數
        self.TotalShare = totalShare
        # 成交金額
        self.TotalTurnover = totalTurnover
        # 開盤價
        self.OpenPrice = openPrice
        # 最高價
        self.HighestPrice = highestPrice
        # 最低價
        self.LowestPrice = lowestPrice
        # 收盤價
        self.ClosePrice = closePrice

下載資料並解析出個股每月各交易日盤後資訊

import csv
import datetime
import io

import loguru
import requests

def main(year, month, code):
    date = f'{year}{month:02}01'
    resp = requests.get(
        f'https://www.twse.com.tw/exchangeReport/STOCK_DAY?' +
        f'response=csv&date={date}&stockNo={code}')
    if resp.status_code != 200:
        loguru.logger.error('RESP: status code is not 200')
    loguru.logger.success('RESP: success')

    # 個股每月各交易日盤後資訊清單
    afterHoursDailyInfos = []
    # 取出 CSV 內容,並去除第一行及最後 5 行
    lines = io.StringIO(resp.text).readlines()
    lines = lines[1:-5]
    # 透過 CSV 讀取器載入
    reader = csv.DictReader(io.StringIO('\n'.join(lines)))
    # 依序取出每筆資料行
    for row in reader:
        # 取出日期欄位值
        date = row['日期'].strip()
        # 取出成交股數欄位值
        totalShare = row['成交股數'].replace(',', '').strip()
        # 取出成交金額欄位值
        totalTurnover = row['成交金額'].replace(',', '').strip()
        # 取出開盤價欄位值
        openPrice = row['開盤價']
        # 取出最高價欄位值
        highestPrice = row['最高價']
        # 取出最低價欄位值
        lowestPrice = row['最低價']
        # 取出收盤價欄位值
        closePrice = row['收盤價']
        afterHoursDailyInfo = AfterHoursDailyInfo(
            code=code,
            date=date,
            totalShare=totalShare,
            totalTurnover=totalTurnover,
            openPrice=openPrice,
            highestPrice=highestPrice,
            lowestPrice=lowestPrice,
            closePrice=closePrice
        )
        afterHoursDailyInfos.append(afterHoursDailyInfo)
    loguru.logger.info(afterHoursDailyInfos)

    # 將每筆物件表達式輸出的字串以系統換行符號相接,讓每筆物件表達式各自獨立一行
    message = os.linesep.join([
        str(afterHoursDailyInfo)
        for afterHoursDailyInfo in afterHoursDailyInfos
    ])
    loguru.logger.info('AFTERHOURSDAILYINFOS' + os.linesep + message)

if __name__ == '__main__':
    loguru.logger.add(
        f'{datetime.date.today():%Y%m%d}.log',
        rotation='1 day',
        retention='7 days',
        level='DEBUG'
    )
    # 傳入年、月及股票代碼
    main(2019, 4, '1101')

重新定義資料儲存結構

因為存入資料都是字串,但實際內容有整數(成交股數、成交金額),還有浮點數(開盤價、最高價、最低價、收盤價),所以需要在儲存類別的建構子中進行轉換處理

import fractions

class AfterHoursDailyInfo:
    def __init__(
        self,
        code,
        date,
        totalShare,
        totalTurnover,
        openPrice,
        highestPrice,
        lowestPrice,
        closePrice):
        # 代碼
        self.Code = code
        # 日期
        # 國曆年轉為西元年
        parts = date.split('/')
        date = datetime.date(int(parts[0]) + 1911, int(parts[1]), int(parts[2]))
        self.Date = date
        # 成交股數
        self.TotalShare = int(totalShare)
        # 成交金額
        self.TotalTurnover = int(totalTurnover)
        # 開盤價
        self.OpenPrice = fractions.Fraction(openPrice)
        # 最高價
        self.HighestPrice = fractions.Fraction(highestPrice)
        # 最低價
        self.LowestPrice = fractions.Fraction(lowestPrice)
        # 收盤價
        self.ClosePrice = fractions.Fraction(closePrice)
    # 物件表達式
    def __repr__(self):
        return (
            f'class AfterHoursDailyInfo {{ '
            f'Code={self.Code}, '
            f'Date={self.Date:%Y-%m-%d}, '
            f'TotalShare={self.TotalShare}, '
            f'TotalTurnover={self.TotalTurnover}, '
            f'OpenPrice={float(self.OpenPrice):.2f}, '
            f'HighestPrice={float(self.HighestPrice):.2f}, '
            f'LowestPrice={float(self.LowestPrice):.2f}, '
            f'ClosePrice={float(self.ClosePrice):.2f} '
            f'}}'
        )

重新執行得到輸出結果。
執行結果

Would You Like To Know More?

https://docs.python.org/3/library/csv.html


團隊系列文:

CSScoke - 金魚都能懂的這個網頁畫面怎麼切 - 金魚都能懂了你還怕學不會嗎
Clarence - LINE bot 好好玩 30 天玩轉 LINE API
Hina Hina - 陣列大亂鬥
King Tzeng - IoT沒那麼難!新手用JavaScript入門做自己的玩具
Vita Ora - 好 Js 不學嗎 !? JavaScript 入門中的入門。
TaTaMo - 用Python開發的網頁不能放到Github上?Lektor說可以!!


上一篇
Day-07 資料蒐集:取得個股盤後資訊 + 解析 JSON
下一篇
Day-09 資料蒐集:取得個股當日基本資訊 + 解析 PDF
系列文
Python 程式交易 30 天新手入門30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
kyc1109
iT邦新手 4 級 ‧ 2019-10-02 17:13:52
    # 取出收盤價欄位值
    closePrice = row['收盤價']
    afterHoursDailyInfo = AfterHoursDailyInfo(
        code=code,
        date=date,

上面的程式,
afterHoursDailyInfo = AfterHoursDailyInfo(
要改成,
afterHoursDailyInfo = AfterHoursInfo (
不然會出現錯誤。

感謝斬大的教學。

阿斬 iT邦新手 5 級 ‧ 2019-10-03 06:40:09 檢舉

抱歉,是我重新定義儲存結構那裡宣告類別名稱錯誤,已修正,感謝

我要留言

立即登入留言