本节代码将每天获取所有股票的数据(开、高、低、收盘价,前收盘价,成交量),结合股票历史数据(也可以不结合,只从当天开始),构建完整的实时股票日数据库,保存为csv格式。
下载数据用python自带的urllib库,处理数据用pandas库。
可在每天15点以后运行,更新所有股票的日数据。
from urllib.request import urlopen # python自带爬虫库
import pandas as pd
from datetime import datetime
import time
import re # 正则表达式库
import os # 系统库
import json # python自带的json数据库
pd.set_option(\'expand_frame_repr\', False) # 当列太多时不换行
pd.set_option(\'display.max_rows\', 500) # 最多显示数据的行数
# =====函数:从网页上抓取数据,返回抓取到的网页内容
def get_content_from_internet(url, max_try_num=10, sleep_time=5):
"""
使用python自带的urlopen函数,从网页上抓取数据
:param url: 要抓取数据的网址
:param max_try_num: 最多尝试抓取次数
:param sleep_time: 每次抓取失败后停顿的时间
:return: 返回抓取到的网页内容
"""
get_success = False # 是否成功抓取到内容
# 抓取内容
for i in range(max_try_num):
try:
content = urlopen(url=url, timeout=15).read() # 使用python自带的库,从网络上获取信息
get_success = True # 成功抓取到内容
break
except Exception as e:
print(\'抓取数据报错,次数:\', i+1, \'报错内容:\', e)
time.sleep(sleep_time)
# 判断是否成功抓取内容
if get_success:
return content
else:
raise ValueError(\'使用urlopen抓取网页数据不断报错,达到尝试上限,停止程序,请尽快检查问题所在\')
# =====函数:从新浪获取指定股票(可多只,可以是指数)最近一个交易日的数据,整理成一定格式的DataFrame,返回这个DataFrame
def get_today_data_from_sinajs(code_list):
"""
返回一串股票最近一个交易日的相关数据
从这个网址获取股票数据:http://hq.sinajs.cn/list=sh600000,sz000002,sz300001
正常网址:https://finance.sina.com.cn/realstock/company/sh600000/nc.shtml,
:param code_list: 一串股票代码的list,可以多个,例如[sh600000, sz000002, sz300001],
:return: 返回一个存储股票数据的DataFrame
"""
# 构建url
url = "http://hq.sinajs.cn/list=" + ",".join(code_list) #先用逗号把code_list中的元素连接起来,再在前面加上http:……
# 抓取数据
content = get_content_from_internet(url)
content = content.decode(\'gbk\') #用gbk解码,可解码出中文;解码前是dytes,解码后是str
# 将数据转换成DataFrame
content = content.strip() # 去掉文本前后的空格、回车等
data_line = content.split(\'\n\') # 每行是一个股票的数据,生成一个列表
data_line = [i.replace(\'var hq_str_\', \'\').split(\',\') for i in data_line] #去掉每行前面的冗余数据,并再次分割,形成2层列表
# df = pd.DataFrame(data_line, dtype=\'float\') #dtype=\'float\'可生成数值型数据
df = pd.DataFrame(data_line) #这里只生成DF,转float放在下面
# 对DataFrame进行整理
df[0] = df[0].str.split(\'="\') #df[0]是取“0”列
df[\'stock_code\'] = df[0].str[0].str.strip()
df[\'stock_name\'] = df[0].str[-1].str.strip()
df[\'candle_end_time\'] = df[30] + \' \' + df[31] # 股票市场的K线,是普遍以当跟K线结束时间来命名的
df[\'candle_end_time\'] = pd.to_datetime(df[\'candle_end_time\'])
rename_dict = {1: \'open\', 2: \'pre_close\', 3: \'close\', 4: \'high\', 5: \'low\', 6: \'buy1\', 7: \'sell1\',
8: \'amount\', 9: \'volume\', 32: \'status\'} # 自己去对比数据,会有新的发现;10~29是买卖五档盘口的数据
# 其中amount单位是股,volume单位是元
df.rename(columns=rename_dict, inplace=True)
#挑选需要转化的列,转化为float
df[[\'open\', \'high\', \'low\', \'close\', \'pre_close\', \'amount\',\'volume\', \'buy1\', \'sell1\']] \
= df[[\'open\', \'high\', \'low\', \'close\', \'pre_close\', \'amount\', \'volume\', \'buy1\', \'sell1\']].astype(\'float\')
df[\'status\'] = df[\'status\'].str.strip(\'";\') #在status列,去掉冗余的‘";’字符
df = df[[\'stock_code\', \'stock_name\', \'candle_end_time\', \'open\', \'high\', \'low\', \'close\', \'pre_close\', \'amount\',
\'volume\', \'buy1\', \'sell1\', \'status\']] #重新规定列顺序
return df
# =====函数:判断今天是否是交易日
def is_today_trading_day():
"""
判断今天是否是交易日
:return: 如果是返回True,否则返回False
"""
# 获取上证指数今天的数据
df = get_today_data_from_sinajs(code_list=[\'sh000001\'])
sh_date = df.iloc[0][\'candle_end_time\'] # 上证指数最近交易日
# 判断今天日期和sh_date是否相同
return datetime.now().date() == sh_date.date()
# df = get_today_data_from_sinajs([\'sh000001\',\'sh600000\', \'sz000002\', \'sz300001\'])
# print(df)
# =====函数:从新浪获取所有股票最近一个交易日的数据,返回一个DF
def get_all_today_stock_data_from_sina_marketcenter():
"""
http://vip.stock.finance.sina.com.cn/mkt/#stock_hs_up
从新浪网址的上述的网址,逐页获取最近一个交易日所有股票的数据
:return: 返回一个存储股票数据的DataFrame
"""
# ===数据网址
raw_url = \'http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?page=%s\' \
\'&num=80&sort=symbol&asc=1&node=hs_a&symbol=&_s_r_a=sort\'
page_num = 1
# ===存储数据的DataFrame
all_df = pd.DataFrame()
# ===获取上证指数最近一个交易日的日期。此段代码在课程视频中没有,之后补上的
df = get_today_data_from_sinajs(code_list=[\'sh000001\'])
sh_date = df.iloc[0][\'candle_end_time\'].date() # 上证指数最近交易日
# ===开始逐页遍历,获取股票数据
while True:
# 构建url
url = raw_url % (page_num)
print(\'开始抓取页数:\', page_num)
# 抓取数据
content = get_content_from_internet(url)
content = content.decode(\'gbk\')
# 判断页数是否为空
if \'null\' in content:
print(\'抓取到页数的尽头,退出循环\')
break
# 通过正则表达式,给key加上引号
content = re.sub(r\'(?<={|,)([a-zA-Z][a-zA-Z0-9]*)(?=:)\', r\'"\1"\', content)
# 将数据转换成dict格式
content = json.loads(content) #此时,content转化为list,其中的元素是dict
# 将数据转换成DataFrame格式
df = pd.DataFrame(content, dtype=\'float\')
# 对数据进行整理
# 重命名
rename_dict = {\'symbol\': \'股票代码\', \'name\': \'股票名称\', \'open\': \'开盘价\', \'high\': \'最高价\', \'low\': \'最低价\',
\'trade\': \'收盘价\', \'settlement\': \'前收盘价\', \'volume\': \'成交量\', \'amount\': \'成交额\'}
df.rename(columns=rename_dict, inplace=True) #没映射的列,还是保留原来的列名,即列数不会减少
# 添加交易日期
# df[\'交易日期\'] = pd.to_datetime(datetime.now().date()) # 课程视频中使用的是本行代码
df[\'交易日期\'] = pd.to_datetime(sh_date) # 在课程视频中使用的是上一行代码,现在改成本行代码,程序更加稳健
# df中的字段,还包括:mktcap(总市值)、nmc(流通值)、per(本益比,类似市盈率)、pb(市净率)、turnoverratio(换手率)等
# 取需要的列
df = df[[\'股票代码\', \'股票名称\', \'交易日期\', \'开盘价\', \'最高价\', \'最低价\', \'收盘价\', \'前收盘价\', \'成交量\', \'成交额\']]
# 合并数据
all_df = all_df.append(df, ignore_index=True)
# 将页数+1
page_num += 1
time.sleep(1)
# ===将当天停盘的股票删除,此段代码在课程视频中没有,之后补上的
all_df = all_df[all_df[\'开盘价\'] - 0 > 0.00001]
all_df.reset_index(drop=True, inplace=True)
# ===返回结果
return all_df
# 更新数据思路2:
# 1. 使用get_all_today_stock_data_from_sina_marketcenter(),一下子获取所有股票数据,
# 将数据输出到预测者网的数据中
# 以下为主程序
# 判断今天是否是交易日
if is_today_trading_day() is False:
print(\'今天不是交易日,不需要更新股票数据,退出程序\')
exit()
# 判断当前时间是否超过15点
if datetime.now().hour < 15: # 保险起见可以小于16点
print(\'今天还没到15点,不更新股票数据,退出程序\')
exit()
# 获取今天所有的股票数据
df = get_all_today_stock_data_from_sina_marketcenter()
# 对数据进行存储
for i in df.index:
t = df.iloc[i:i+1, :]
stock_code = t.iloc[0][\'股票代码\']
# 构建存储文件路径
path = r\'C:\Users\lori\Desktop\stockinvest\project1\data\stock_database\basic_trading_data.20200210\stock_data\'\
+ \'\\\' + stock_code + \'.csv\' #注意在前面要加r,或者把\换成\\或者/
# 文件存在,不是新股
if os.path.exists(path):
t.to_csv(path, header=None, index=False, mode=\'a\', encoding=\'gbk\')
# 文件不存在,说明是新股
else:
# 先将头文件输出
pd.DataFrame(columns=[\'数据由lori整理\']).to_csv(path, index=False, encoding=\'gbk\')
t.to_csv(path, index=False, mode=\'a\', encoding=\'gbk\')
print(stock_code)