一、财报数据源(python)
有两大块数据需要入库:上市公司基本信息、上市公司报表。
1.1 上市公司的基本信息
通过tushare库可以直接获取A股上市公司的基本信息,详见:tushare.pro/document/2?… 注册后获取自己的【接口token】
代码如下:
import tushare
pro = tushare.pro_api(接口token)
allStockSourceData = pro.query('stock_basic', exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date')
以上得到的allStockSourceData数据结构是DataFrame,它的columns可以读出query方法里传入的fields的6个字段,即:ts_code,symbol,name,area,industry,list_date。这些(加上start_date、end_date)将作为数据库的字段名,写入到上市公司基本信息表里。而allStockSourceData里的vaules,则包含了所有股票对应的6个数据。创建数据库代码片段、效果如下:
def createDB(dbName, dbcursor):
try:
createDBSentence = "create database if not exists " + dbName
dbcursor.execute(createDBSentence)
dbcursor.execute("use " + dbName + ";")
# print('create db', dbName, ' successfully!')
except Exception as e:
print('create db {} fail , e={}'.format(dbName, e))
问题1、为什么从tushare取得6个字段,还要加上 start_date、end_date 两个字段呢?
- 每家公司的财报,都有起始时间、目前最新报表时间。前端显示时,让用户选取年份报表,需要有选择范围,所以需要这两个字段。如下图:
问题2、这两个字段的数据又是怎么来的呢?
- 后面将爬取每个公司历年来所有报表,取最久远的日期作为start_date的值,最新的作为end_date的值*
问题3、得到这两个数据又怎么加入数据库呢?
- python直接更新数据库:
"update all_stock_info set start_date='%s', end_date='%s' where symbol='%s';" % (s, e, code)
1.2 三大报表
本节三大重点:1、下载报表;2、读报表文件-解析数据-写入数据库;3、创建标题名字文件(前端用来显示)
1.2.1 下载报表
三大报表在tushare上数据很丰富,但要很高积分才能调取接口。所以……
于是找了免费的数据源 -- 网易财经。三大报表地址如下(以“平安银行,symbol=000001为例):
利润表:quotes.money.163.com/service/lrb…
资产负债表:quotes.money.163.com/service/zcf…
现金流量表:quotes.money.163.com/service/xjl…
现在三大表地址已经齐了,全部上市公司基本信息也有了(即有了symbol),接下来遍历上文 1.1 拿到的所有上市公司数据,取其symbol,串成这家公司的三张报表地址,python把所有报表下载下来,数据源就齐了!就等入库!
代码片断:
def downloadReport(stockList, url, path):
failList = []
for symbol in stockList:
loadUrl = str(url).replace('%name%', str(symbol))
fileName = str(symbol)
while True:
try:
savePath = str(path).replace('%name%', fileName)
urllib.request.urlretrieve(loadUrl, savePath)
time.sleep(0.3)
break
except Exception as e:
failList.append(fileName)
if str(e) == 'HTTP Error 404: Not Found':
break
else:
print('load {} fail ~ {}'.format(str(symbol), e))
continue
incomeUrl = "http://quotes.money.163.com/service/lrb_%name%.html"
incomeFolder = "./income_statements/income_%name%.csv"
downloadReport(stockList, incomeUrl,incomeFolder)
1.2.2 读报表文件-解析数据-写入数据库
三大报表都下载完成,开始解析这些表,并写入数据库
1、数据库结构
三大表对应三个数据库(资产负债表数据库finance_report_balance、利润表数据库finance_report_income、现金流量表数据库finance_report_cash。),全部公司的三大报表各自写到对应库中。创建数据库请看上文createDB方法。
2、数据入库 三大表文件格式为csv,python直接读表,思路如下(以利润表为例):
(1)得到数据库字段名insertTitleHead:数据库title转为英文,因为所有公司的同一类型报表,此列所有字段都一样且全部齐全,所以直接按顺序从上到下把第一个【报告日期】译成"report_date"之后,其他所有译成income_1、income_2......(资产负债表balance_1、balance_2...;现金流量表cash_1、cash_2...),而每个字段对应的中文写到一个文件里,这就是下文《1.2.3 创建标题名字文件》。
(2)得到数据库全部数据:除了第一列作为字段名,剩余的就是一条数据的内容。每条数据数量、值与上面insertTitleHead(report_date、income_1、income_2...)一一对应。
(3)、写入数据库(写入title、data):
创建表~create table if not exists income_000001 (report_date VARCHAR(767), primary key (report_date), income_1 float, income_2 float, ..., income_45 float)
写入单条data~insert into income_000001 (report_date, income_1, income_2, ..., income_45) values ("2020-12-31", 15354200, null, ..., 1.4)
重复写入全部data
写完后数据库如下:
请参照源码:
1.2.3 创建标题名字文件(前端用来显示)
每解析一种表,都会保存第一列标题到一个字典里。
资产负债表:{"report_date":"报告日期","balance_1":"货币资金(万元)","balance_2":"结算备付金(万元)",...}
利润表:{"report_date":"报告日期","income_1":"营业总收入(万元)","income_2":"营业收入(万元)",...}
现金流量表:{"report_date":"报告日期","cash_1":" 销售商品、提供劳务收到的现金(万元)","cash_2":" 客户存款和同业存放款项净增加额(万元)",...}
当全部表完成了,合并三个对象,并转成report_title.json保存下来,前端显示需要先加载此文件。服务器只返回数据表的字段名:income_1, income_2, ..., web端需要把这些字段名转译成中文。
问题:
(1)为什么这些字段直接用_1、2这样,而不是直接翻译中文意思或是用拼音呢? ~~因为简洁一点,如果用翻译,工作量太大,如果以后再一加项指标,就需要人工再去翻译,而做不到自动化了。不翻译以后加一项,就会加一个income** : "中文意思",pyhon照常入库,java照常读表,web照常读json解析对应中文。
(2)为什么要写成report_title.json保存下来,而不入库? ~减小服务器的访问量
三、脚本部署
1、crontab定时执行以上的python。
2、如何避免每次都全量执行
第一次执行python,全下载所有报表,并生成数据库。以后只做增量,不能再全量。
避免全量方法:
1、检测是否第一次: 在获取所有上市公司信息的时候,尝试创建table,此时如果table已经存在,python会返回异常,说明已经全量过,此次不用全量,标记isWhole=false。
2、对比每家上市公司上市日期: 如果日期小于今天,则已经下载过。否则是今天上市,需要将此公司入库详细信息数据库,并且尝试去下载此公司的三大报表(确定上市当天没有报表的话就不用下载)。