通過程式設計擷取曆史股價的方式很多,但是免費的方式可以簡單地分為四類:
- 使用某種網友開發的插件: 比如Python就有TuShare, akshare, 這種隻需要寫幾行代碼就能擷取,但是不同的庫有不同的利弊。比如TuShare中有很多資料需要一定的積分才能擷取。
- 通過API接口,比如新浪,騰訊等等。
- 從專門的行情軟體上面提取資料。
- 從财經網站上面爬取行情資料。
使用TuShare擷取股價
TuShare是非常友善使用的, 比如在官網拿到Token後, 一行調用就可以擷取股價資訊:
import tushare as ts
ts.set_token('***')
pro = ts.pro_api()
price = ts.pro_bar(ts_code='000020.SZ', start_date='2022-09-01',\
end_date='2022-09-09')
price
結果如圖:
通過API擷取價格資訊
新浪API
接口 | 描述 |
http://hq.sinajs.cn/list= + 股票代碼 | 當日行情 |
http://hq.sinajs.cn/list=s_+ 股票代碼 | 實時行情 |
請求例子:
import requests
stock_code = 'sh600031,sz300866'
header = { 'referer': 'http://finance.sina.com.cn' }
# 當日行情接口
response = requests.get(f'http://hq.sinajs.cn/list={stock_code}', headers=header)
print(response.text)
結果
var hq_str_sh600031="三一重工,15.750,15.750,16.260,16.310,15.660,16.260,16.270,
105649444,1702155639.000,6161,16.260,870579,16.250,132500,16.240,87700,16.230,
103100,
16.220,400510,16.270,328600,16.280,198700,16.290,795400,16.300,548100,16.310,2022-09-09,15:00:00,00,";var hq_str_sz300866="安克創新,59.250,58.620,59.150,59.420,57.850,59.150,59.160,
1703977,100420830.250,700,59.150,300,59.140,300,59.130,2000,59.120,7400,59.110,
300,
59.160,2000,59.170,2700,59.190,700,59.200,400,59.250,2022-09-09,15:35:00,00,D|0|0.000";
以上資料用,分割字元串中的内容,下标從0開始,依次為:
标号 | 内容 | 标号 | 内容 |
股票名字 | 1 | 當天開盤價 | |
2 | 昨日收盤價 | 3 | 當天收盤價 |
4 | 當天最高價 | 5 | 當天最低價 |
... |
指數也可以用這種方式擷取。但是無法擷取曆史的股價資訊。如果某天沒有及時同步到本地資料庫,就無法使用該接口擷取那天的資料。接口可以一定性請求多個股票代碼,用,隔開,不能有空格。
另外,這個接口現在一定要添加請求頭:
header = { 'referer': 'http://finance.sina.com.cn' }
不然就會報錯,提示:
Kinsoku jikou desu!
也就是日本語“禁止通路”的意思。很藝文!
騰訊API
接口 | 描述 |
http://qt.gtimg.cn/q= + 股票代碼 | 最新行情 |
http://qt.gtimg.cn/q=ff_ + 股票代碼 | 擷取實時資金流向 |
http://qt.gtimg.cn/q=s_pk + 股票代碼 | 盤口分析 |
http://qt.gtimg.cn/q=s_sh + 股票代碼 | 簡要資訊 |
請求例子:
import requests
stock_code = 'sh600031,sz300866'
# 當日行情接口
response = requests.get(f'http://qt.gtimg.cn/q={stock_code}', timeout=6)
print(response.text)
結果
v_sh600031="1~三一重工~600031~16.26~15.75~15.75~1056494~612059~444435~16.26~
62~16.25~8706~16.24~1325~16.23~877~16.22~1031~16.27~4005~16.28~3286~16.29~1987~
16.30~7954~16.31~5481~~20220909160001~0.51~3.24~16.31~15.66~
16.26/1056494/1702155639~1056494~170216~1.24~30.06~~16.31~15.66~4.13~1381.01~
1381.01~2.20~17.33~14.18~2.47~-10712~16.11~26.21~11.48~~~1.51~170215.5639~0.0000~
0~ ~GP-A~-27.26~4.16~2.75~7.32~3.17~28.30~14.13~4.77~-3.21~-9.62~8493286021~
8493286021~-30.86~-29.80~8493286021~~~-41.60~0.06~";
v_sz300866="51~安克創新~300866~59.15~58.62~59.25~17040~9711~7329~59.15~7~59.14~3~
59.13~3~59.12~20~59.11~74~59.16~3~59.17~20~59.19~27~59.20~7~59.25~4~~20220909161451~
0.53~0.90~59.42~57.85~59.15/17040/100420830~17040~10042~1.13~20.92~~59.42~57.85~
2.68~89.46~240.40~3.82~70.34~46.90~0.70~46~58.93~20.88~24.49~~~1.45~10042.0830~
0.0000~0~ AR~GP-A-CYB~-41.84~-3.30~1.35~18.25~13.07~128.07~48.89~-6.41~-13.66~
-5.83~151240045~406427207~27.38~-46.32~151240045~~~-45.35~-0.03~";
以上資料用~分割字元串中的内容,下标從0開始,依次為:
标号 | 内容 | 标号 | 内容 |
未知 | 1 | 股票名字 | |
2 | 股票代碼 | 3 | 目前價格 |
4 | 昨收 | 5 | 今開 |
6 | 成交量(手) | 7 | 外盤 |
8 | 内盤 | 9 | 買一 |
10 | 買一量(手) | 11-18 | 買二 |
19 | 賣一 | 20 | 賣一量 |
21-28 | 賣二 | 29 | 最近逐筆成交 |
30 | 時間 | 31 | 漲跌 |
32 | 漲跌% | 33 | 最高 |
34 | 最低 | 35 | 價格/成交量(手)/成交額 |
36 | 成交量(手) | 37 | 成交額(萬) |
38 | 換手率 | 39 | 市盈率 |
40 | 41 | 最高 | |
42 | 最低 | 43 | 振幅 |
44 | 流通市值 | 45 | 總市值 |
46 | 市淨率 | 47 | 漲停價 |
48 | 市淨率 |
弊端就是哪天接口被禁用了,這種方式就用不了了。
從專門的行情軟體上面提取資料
通達信
通達信是使用非常廣泛的行情軟體,其公司财富趨勢已經在港交所上市。在通達信軟體上下載下傳行情後,資料會下載下傳到本地(至少此時2022-09-10是這樣)。資料在通達信的安裝目錄下:
可以使用程式讀出來。附完整解析代碼:
import struct
import datetime
class stock_price:
def __init__(self, stock_date, stock_open, stock_high,\
stock_low, stock_close, stock_amount, stock_vol):
self.stock_date = stock_date
self.stock_open = stock_open
self.stock_high = stock_high
self.stock_low = stock_low
self.stock_close = stock_close
self.stock_amount = stock_amount
self.stock_vol = stock_vol
def get_stock_price(filepath):
data = []
with open(filepath, 'rb') as f:
while True:
stock_date = f.read(4)
stock_open = f.read(4)
stock_high = f.read(4)
stock_low= f.read(4)
stock_close = f.read(4)
stock_amount = f.read(4)
stock_vol = f.read(4)
stock_reservation = f.read(4)
if not stock_date:
break
stock_date = struct.unpack("l", stock_date) # 4位元組 如20220909
stock_open1 = struct.unpack("l", stock_open) #開盤價*100
stock_high1 = struct.unpack("l", stock_high) #最高價*100
stock_low= struct.unpack("l", stock_low) #最低價*100
stock_close = struct.unpack("l", stock_close) #收盤價*100
stock_amount = struct.unpack("f", stock_amount) #成交額
stock_vol = struct.unpack("f", stock_vol) # 成交量
stock_reservation = struct.unpack("f", stock_reservation) #保留值
date_format = datetime.datetime.strptime(str(stock_date[0]),\
'%Y%M%d') #格式化日期
data.append(stock_price(date_format.strftime('%Y-%M-%d'),
stock_open1[0]/100.0,
stock_high1[0]/100.0,\ stock_low[0]/100.0, stock_close[0]/100.0, stock_amount[0]/100.0, \
stock_vol[0]/100.0))
return data
找一個檔案試試:
data_list = get_stock_price('./sz000002.day')
print(f'交易日期: {data_list[-1:][0].stock_date}')
print(f'開盤價: {data_list[-1:][0].stock_open}')
print(f'最高價: { data_list[-1:][0].stock_high}')
print(f'最低價: { data_list[-1:][0].stock_low}')
print(f'收盤價: { data_list[-1:][0].stock_close}')
輸出:
交易日期: 2022-09-09
開盤價: 17.49
最高價: 18.39
最低價: 17.41
收盤價: 18.15
在同花順裡面驗證了下價格資訊,是正确的。不過這種方式的弊端在于,需要手工在通達信裡面執行更新資料的操作,并且通達信在未來某天變更了讀寫檔案的方式,這種方式就不能用了。其它的軟體也是類似的。這裡要注意的是,如果需要讀取指數的資訊,
struct.unpack("l", stock_open)
這行代碼裡面的格式需要變動一下,目前變成
struct.unpack("f", stock_open)
即可。
從财經網站上面爬取行情資料
這種是最靠譜的方式的,但是實作也是比較繁瑣的,某種程度上,也有法律上面的風險,參考:中國爬蟲違法違規案例彙總 (https://github.com/HiddenStrawberry/Crawler_Illegal_Cases_In_China)。股票價格資料都是公開的,不是什麼隐私,但是程式寫的不好,把目标網站爬當機了,就有法律風險了。上面例子中的新浪API接口還是很穩定,支援高并發的。貌似可能是某個性能高手開發的, 參考: 楊建:網站加速--伺服器編寫篇(上)(https://blog.csdn.net/lishenglong666/article/details/39028093), 原文不見了,放個轉載。
提供股價資訊的部分網站
- 雪球, https://xueqiu.com
- 集思錄, https://www.jisilu.cn/
- 巨潮資訊網, http://www.cninfo.com.cn
- 百度股市通, https://gushitong.baidu.com
- 東方财富網, https://www.eastmoney.com/
- 同花順, https://www.10jqka.com.cn/
......