以取得個股盤後資訊為例。
https://www.twse.com.tw/exchangeReport/MI_INDEX?response=json&type=ALLBUT0999&date=20190904
class AfterHoursInfo:
def __init__(
self,
code,
name,
totalShare,
totalTurnover,
openPrice,
highestPrice,
lowestPrice,
closePrice):
# 代碼
self.Code = code
# 名稱
self.Name = name
# 成交股數
self.TotalShare = totalShare
# 成交金額
self.TotalTurnover = totalTurnover
# 開盤價
self.OpenPrice = openPrice
# 最高價
self.HighestPrice = highestPrice
# 最低價
self.LowestPrice = lowestPrice
# 收盤價
self.ClosePrice = closePrice
import datetime
import json
import os
import re
import loguru
import requests
def main():
# 下載當日個股盤後資訊
resp = requests.get(
f'https://www.twse.com.tw/exchangeReport/MI_INDEX?' +
f'response=json&' +
f'type=ALLBUT0999' +
f'&date={datetime.date.today():%Y%m%d}'
)
if resp.status_code != 200:
loguru.logger.error('RESP: status code is not 200')
loguru.logger.success('RESP: success')
# 盤後資訊清單
afterHoursInfos = []
# 取出 JSON 內容
body = resp.json()
# 取出 stat 欄位
stat = body['stat']
# 如果 stat 不是 OK,代表查詢日期尚無資料
if stat != 'OK':
loguru.logger.error(f'RESP: body.stat error is {stat}.')
return
# 取出第 9 表格內容
records = body['data9']
# 依序取出每筆盤後資訊
for record in records:
# 取出代碼欄位值
code = record[0].strip()
# 符合股票代碼規則才處理
if re.match(r'^[1-9][0-9][0-9][0-9]$', code) is not None:
# 取出名稱欄位值
name = record[1].strip()
# 取出成交股數欄位值
totalShare = record[2].replace(',', '').strip()
# 取出成交金額欄位值
totalTurnover = record[4].replace(',', '').strip()
# 取出開盤價欄位值
openPrice = record[5].replace(',', '').strip()
# 取出最高價欄位值
highestPrice = record[6].replace(',', '').strip()
# 取出最低價欄位值
lowestPrice = record[7].replace(',', '').strip()
# 取出收盤價欄位值
closePrice = record[8].replace(',', '').strip()
afterHoursInfo = AfterHoursInfo(
code=code,
name=name,
totalShare=totalShare,
totalTurnover=totalTurnover,
openPrice=openPrice,
highestPrice=highestPrice,
lowestPrice=lowestPrice,
closePrice=closePrice
)
afterHoursInfos.append(afterHoursInfo)
# 將每筆物件表達式輸出的字串以系統換行符號相接,讓每筆物件表達式各自獨立一行
message = os.linesep.join([
str(afterHoursInfo)
for afterHoursInfo in afterHoursInfos
])
loguru.logger.info('AFTERHOURSINFOS' + 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()
因為存入資料都是字串,但實際內容有整數(成交股數、成交金額),還有浮點數(開盤價、最高價、最低價、收盤價),所以需要在儲存類別的建構子中進行轉換處理。
import fractions
class AfterHoursInfo:
def __init__(
self,
code,
name,
totalShare,
totalTurnover,
openPrice,
highestPrice,
lowestPrice,
closePrice):
# 代碼
self.Code = code
# 名稱
self.Name = name
# 成交股數
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):
totalShare = self.TotalShare
if totalShare is not None:
totalShare = f'{totalShare:.2f}'
totalTurnover = self.TotalTurnover
if totalTurnover is not None:
totalTurnover = f'{totalTurnover:.2f}'
openPrice = self.OpenPrice
if openPrice is not None:
openPrice = f'{openPrice:.2f}'
highestPrice = self.HighestPrice
if highestPrice is not None:
highestPrice = f'{highestPrice:.2f}'
lowestPrice = self.LowestPrice
if lowestPrice is not None:
lowestPrice = f'{lowestPrice:.2f}'
closePrice = self.ClosePrice
if closePrice is not None:
closePrice = f'{closePrice:.2f}'
return (
f'class AfterHoursInfo {{ '
f'Code={self.Code}, '
f'Name={self.Name}, '
f'TotalShare={totalShare}, '
f'TotalTurnover={totalTurnover}, '
f'OpenPrice={openPrice}, '
f'HighestPrice={highestPrice}, '
f'LowestPrice={float(lowestPrice):.2f}, '
f'ClosePrice={closePrice} '
f'}}'
)
但執行後發現與前一日相同數值者會標示為 --,造成轉型錯誤,所以重新修正。
import datetime
import fractions
import json
import os
import re
import loguru
import requests
class AfterHoursInfo:
def __init__(
self,
code,
name,
totalShare,
totalTurnover,
openPrice,
highestPrice,
lowestPrice,
closePrice):
# 代碼
self.Code = code
# 名稱
self.Name = name
# 成交股數
self.TotalShare = self.checkNumber(totalShare)
if self.TotalShare is not None:
self.TotalShare = int(totalShare)
# 成交金額
self.TotalTurnover = self.checkNumber(totalTurnover)
if self.TotalTurnover is not None:
self.TotalTurnover = int(totalTurnover)
# 開盤價
self.OpenPrice = self.checkNumber(openPrice)
if self.OpenPrice is not None:
self.OpenPrice = fractions.Fraction(openPrice)
# 最高價
self.HighestPrice = self.checkNumber(highestPrice)
if self.HighestPrice is not None:
self.HighestPrice = fractions.Fraction(highestPrice)
# 最低價
self.LowestPrice = self.checkNumber(lowestPrice)
if self.LowestPrice is not None:
self.LowestPrice = fractions.Fraction(lowestPrice)
# 收盤價
self.ClosePrice = self.checkNumber(closePrice)
if self.ClosePrice is not None:
self.ClosePrice = fractions.Fraction(closePrice)
# 物件表達式
def __repr__(self):
totalShare = self.TotalShare
if totalShare is not None:
totalShare = f'{totalShare}'
totalTurnover = self.TotalTurnover
if totalTurnover is not None:
totalTurnover = f'{totalTurnover}'
openPrice = self.OpenPrice
if openPrice is not None:
openPrice = f'{float(openPrice):.2f}'
highestPrice = self.HighestPrice
if highestPrice is not None:
highestPrice = f'{float(highestPrice):.2f}'
lowestPrice = self.LowestPrice
if lowestPrice is not None:
lowestPrice = f'{float(lowestPrice):.2f}'
closePrice = self.ClosePrice
if closePrice is not None:
closePrice = f'{float(closePrice):.2f}'
return (
f'class AfterHoursInfo {{ '
f'Code={self.Code}, '
f'Name={self.Name}, '
f'TotalShare={totalShare}, '
f'TotalTurnover={totalTurnover}, '
f'OpenPrice={openPrice}, '
f'HighestPrice={highestPrice}, '
f'LowestPrice={lowestPrice}, '
f'ClosePrice={closePrice} '
f'}}'
)
# 檢查數值是否有效
def checkNumber(self, value):
if value == '--':
return None
else:
return value
def main():
resp = requests.get(
f'https://www.twse.com.tw/exchangeReport/MI_INDEX?' +
f'response=json&' +
f'type=ALLBUT0999' +
f'&date={datetime.date.today():%Y%m%d}'
)
if resp.status_code != 200:
loguru.logger.error('RESP: status code is not 200')
loguru.logger.success('RESP: success')
afterHoursInfos = []
body = resp.json()
stat = body['stat']
if stat != 'OK':
loguru.logger.error(f'RESP: body.stat error is {stat}.')
return
records = body['data9']
for record in records:
code = record[0].strip()
if re.match(r'^[1-9][0-9][0-9][0-9]$', code) is not None:
name = record[1].strip()
totalShare = record[2].replace(',', '').strip()
totalTurnover = record[4].replace(',', '').strip()
openPrice = record[5].replace(',', '').strip()
highestPrice = record[6].replace(',', '').strip()
lowestPrice = record[7].replace(',', '').strip()
closePrice = record[8].replace(',', '').strip()
afterHoursInfo = AfterHoursInfo(
code=code,
name=name,
totalShare=totalShare,
totalTurnover=totalTurnover,
openPrice=openPrice,
highestPrice=highestPrice,
lowestPrice=lowestPrice,
closePrice=closePrice
)
afterHoursInfos.append(afterHoursInfo)
message = os.linesep.join([
str(afterHoursInfo)
for afterHoursInfo in afterHoursInfos
])
loguru.logger.info('AFTERHOURSINFOS' + 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()
重新執行得到輸出結果。
https://docs.python.org/3/library/json.html
團隊系列文:
CSScoke - 金魚都能懂的這個網頁畫面怎麼切 - 金魚都能懂了你還怕學不會嗎
Clarence - LINE bot 好好玩 30 天玩轉 LINE API
Hina Hina - 陣列大亂鬥
King Tzeng - IoT沒那麼難!新手用JavaScript入門做自己的玩具
Vita Ora - 好 Js 不學嗎 !? JavaScript 入門中的入門。
TaTaMo - 用Python開發的網頁不能放到Github上?Lektor說可以!!
跟著用最後一個範例,我遇到兩個錯誤,一個是要import os,另一個是試在__init__()中,少了self.在checkNumber的部分。
init(): #裡面有用到checkNumber的部分
self.TotalShare = checkNumber(totalShare)
#要改成
self.TotalShare = self.checkNumber(totalShare)
謝謝樓主的教學與分享。
感謝找到程式碼錯誤之處,我馬上修正,謝謝您