软件应用丨Python量化投资学习报告之一:数据抓取
通过第三方数据平台直接调用api
1. 概述
Tushare是一个免费、开源的python财经数据接口包。主要实现对股票等金融数据从数据采集、清洗加工到数据存储的过程,能够为金融分析人员提供快速、整洁、和多样的便于分析的数据,为他们在数据获取方面极大地减轻工作量,使他们更加专注于策略和模型的研究与实现上。
接口文档地址:http://tushare.org/
ToShare Pro: https://tushare.pro/
2. 基本使用
使用前提:
安装Python
安装Pandas, lxml
下载安装:
方式1:pip install tushare
方式2:访问下述网址下载安装: https://pypi.python.org/pypi/Tushare/
使用:
import tushare as ts
ts.get_hist_data('600848') #一次性获取全部日k线数据
结果显示:
Tushare返回的绝大部分的数据格式都是pandas DataFrame类型,非常便于用pandas / NumPy / Matplotlib进行数据分析和可视化
1. Win.d
官网:https://www.wind.com.cn/Default.html
中国市场的精准金融数据服务供应商,为量化投资与各类金融业务系统提供准确、及时、完整的落地数据,内容涵盖股票、债券、基金、衍生品、指数、宏观行业等各类金融市场数据,助您运筹帷幄,决胜千里。
2. 优矿
官网:https://uqer.io/
提供各类资产的财务、因子、主题、宏观行业特色大数据,以及量化场景下的PIT数据,保障量化过程不引入未来数据。股票、期货、指数、场内外基金等多资产多策略回测。丰富的衍生工具,保证多因子策略、事件驱动等快速实现。
使用爬虫抓取
1. 获取所有股票代码(这里只考虑沪深A股)
http://vip.stock.finance.sina.com.cn/q/go.php/vIR_CustomSearch/index.phtml
2. 获取日线数据
https://finance.sina.com.cn/realstock/company/sh601789/nc.shtml
首先,通过页面元素审查可以发现,分时线是通过HTML5的canvas绘制的,在Sources找相关的js文件,可以找到paintSth.js文件,由于在鼠标移动时会更新页面元素,所以可以直接在文件中查找mousemove,找到了相关代码如下:
C = this.interactCanvas,
...
...
...
C.addEventListener("mousemove", o),
可以看到Canvas加了一个mousemove的监听器, 执行o, 再查找o(), 可以找到如下代码:
function o() {
if (!c) {
var t = document.createElement("canvas");
c = t.getContext("2d")
}
return c
}
这里c又是一个未知量,所以应该继续检索c的信息,由于关联的js文件较多,这种做法过于费时费力,爬取新浪财经的交易数据应该不是明智的选择;
换种思路,既然可以浏览,那么使用 selenium 就有可能,selenium 可以进行自动化测试,让鼠标在固定位置移动,同时抓取更新的信息,这种做法是可行的,我也尝试着做了,由于这一方法过于不实用,源码略;
这种做法局限性太大,首先,效率过低,这是很致命的一个缺陷,除此之外,由于移动导致的像素不同,可能会出现数据遗漏或重复;最后,新浪财经默认只显示一定日期的交易数据,要查看更早的需要手动拖动进度条,这就使得selenium的操作误差更大;
综上所述,在新浪抓取交易数据是挺不容易的事情;
3. 获取财务数据
http://vip.stock.finance.sina.com.cn/q/go.php/vFinanceAnalyze/kind/profit/index.phtml?p=1
1. 获取所有股票代码(这里只考虑沪深A股)
http://quotes.money.163.com/old/#query=EQA&DataType=HS_RANK&sort=PERCENT&order=desc&count=24&page=0
对于网易财经,由于点击换页时页面的url没有更新,所以应该是使用了Ajax或Js来更新数据,通过F12调起开发者工具,在Network选型卡中,筛选XHR,每当点击换页时,就会有新的XHR,分析这些XHR的url可以发现,只有page值在改变:
直接复制Request URL并使用浏览器访问,可以得到json格式的数据,但是可以看到中文通过Unicode编码了,在获取后,可以通过s.decode('unicode_escape')来解码;接下来就是对json解析并提取需要的信息了, json格式如下:
在list中有[0]到[23]共24条数据,对应请求中的参数count=24,关于字段名的解释,以下为我的分析:
'http://quotes.money.163.com/hs/service/diyrank.php?host=http%3A%2F%2Fquotes.money.163.com%2Fhs%2Fservice%2Fdiyrank.php&page=0&query=STYPE%3AEQA&fields=NO%2CSYMBOL%2CNAME%2CPRICE%2CPERCENT%2CUPDOWN%2CFIVE_MINUTE%2COPEN%2CYESTCLOSE%2CHIGH%2CLOW%2CVOLUME%2CTURNOVER%2CHS%2CLB%2CWB%2CZF%2CPE%2CMCAP%2CTCAP%2CMFSUM%2CMFRATIO.MFRATIO2%2CMFRATIO.MFRATIO10%2CSNAME%2CCODE%2CANNOUNMT%2CUVSNEWS&sort=PERCENT&order=desc&count=3607&type=query'
即可返回所有json格式的数据,然后再进行解析,并写入文件,完整代码如下:
import urllib
import json
import csv
from tqdm import tqdm
# 下载器
class Downloader(object):
def __init__(self, url):
self.url = url
def download(self):
html_content = urllib.request.urlopen(self.url).read()
html_content = html_content.decode("utf-8")
return html_content
# 调度器
class Controller(object):
def __init__(self):
self.downloader = None
self.parser = None
self.saver = None
def get_data(self):
url = "http://quotes.money.163.com/hs/service/diyrank.php?host=http%3A%2F%2Fquotes.money.163.com%2Fhs%2Fservice%2Fdiyrank.php&page=0&query=STYPE%3AEQA&fields=NO%2CSYMBOL%2CNAME%2CPRICE%2CPERCENT%2CUPDOWN%2CFIVE_MINUTE%2COPEN%2CYESTCLOSE%2CHIGH%2CLOW%2CVOLUME%2CTURNOVER%2CHS%2CLB%2CWB%2CZF%2CPE%2CMCAP%2CTCAP%2CMFSUM%2CMFRATIO.MFRATIO2%2CMFRATIO.MFRATIO10%2CSNAME%2CCODE%2CANNOUNMT%2CUVSNEWS&sort=PERCENT&order=desc&count=3607&type=query"
html_content = urllib.request.urlopen(url).read()
# 这时候解码可能导致json解析错误!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# html_content = html_content.decode("unicode_escape")
# 当数量大于2995会报错, json解析失败, 原因是编号为2996的股票, 在公告中嵌套了引号导致json解析失败
data = json.loads(html_content)
self.saver = Saver(data=data)
self.saver.save()
class Saver(object):
def __init__(self, data):
self.data = data
self.prifix = 'F:\\files\\sharesDatas\\code_list\\' # 存放目录
def save(self):
# 新建文件, 写入文件头
file_header = ["代码", "名称", '流通市值', '每股收益', '总市值']
csv_file = open(self.prifix+'沪深A股.csv', 'w', newline='')
writer = csv.writer(csv_file)
writer.writerow(file_header)
list = self.data['list']
sum = len(list)
for i in tqdm(range(0, sum)):
item = list[i]
# 处理股票代码, 去掉网易财经的0/1前缀, 并使其在Excel中显示正常(加`)
code = str(item['CODE'])
code = code[1:7] if len(code) == 7 else code[:]
code = "`" + code
row = [code, item['NAME'], item['MCAP'], item['MFSUM'], item['TCAP']]
csv_file = open(self.prifix + '沪深A股.csv', 'a', newline='') # 追加
writer = csv.writer(csv_file)
writer.writerow(row)
if __name__ == '__main__':
controller = Controller()
controller.get_data()
2. 获取日线数据
http://quotes.money.163.com/trade/lsjysj_601318.html#06f01
其中已经包含了请求的真实地址,即:
"http://quotes.money.163.com/service/chddata.html?code=0601318&start=20070301&end=20190426&fields=TCLOSE;HIGH;LOW;TOPEN;LCLOSE;CHG;PCHG;TURNOVER;VOTURNOVER;VATURNOVER;TCAP;MCAP"
而想要搞清楚url的参数,可以转到submit的文件,这里即b.667271.min.js:1,这是一个压缩的js文件,格式化后,在最后可以找到如下关键代码:
submit: function() {
var e = n.value;
if (e) e = e.replace(/-/g, "");
var a = i.value;
if (a) a = a.replace(/-/g, "");
var o = t.elem.getElementsByTagName("input"),
r = [];
for (var d = 0; d < o.length; d++) {
if (o[d].type == "checkbox" && o[d].checked) {
r.push(o[d].value)
}
}
var c = "/service/chddata.html?code=" + window["STOCKCODE"];
e && /^\d{8}$/.test(e) && (c += "&start=" + e);
a && /^\d{8}$/.test(a) && (c += "&end=" + a);
r.length && (c += "&fields=" + r.join(";"));
location.href = c
}
能够清楚地看到url的拼接过程,具体参数如下:
接下来是做抓取,抓取就是根据已经获取的股票代码,枚举股票代码并下载对应的日线数据,仅需注意每次循环最好使用time.sleep(random.random()*2),否则可能因操作频繁被拒绝访问;下面是部分源代码(注:这里源代码中的股票代码是实时获取的,抓取的是股城网,因为只需要抓取股票代码即可):
import random
import time
from stock.classes.Downloader import Downloader
from stock.classes.Parser import Parser
from stock.classes.Saver import Saver
# 控制器
class Controller(object):
# 构造函数
def __init__(self, url, kline_filepath, codelist_filepath, date):
'''
:param url: 股票基本信息url( 股城网>行情>沪深A股 )
:param kline_filepath: 日线数据文件保存路径
:param codelist_filepath: 股票代码文件保存路径
:param date: 查询截止日期
'''
self.url = url
self.kline_filepath = kline_filepath
self.codelist_filepath = codelist_filepath
self.date = date
self.downloader = None # 下载器实例
self.parser = None # 解析器实例
self.saver = Saver() # 存储器实例
# 执行函数
def start(self):
page = 1
all_code = []
while page <= 181:
print("当前为第%s页" % page)
time.sleep(random.random()*2)
self.downloader = Downloader(self.url % page)
page += 1
html_content = self.downloader.download()
self.parser = Parser(html_content)
codeList = self.parser.parseSharesCode()
for code in codeList:
all_code.append(code)
# print(codeList)
self.saver.saveKlineToCSV(codeList=codeList, filepath=self.kline_filepath, date=self.date)
self.save(codelist=all_code)
def save(self,codelist):
self.saver.saveCodeListToCSV(codeList=codelist, filepath=self.codelist_filepath) # 保存代码到csv
# self.saver.saveKlineToMySQL(filepath=self.kline_filepath) # 保存所有股票的信息到数据库
# 程序入口
if __name__ == '__main__':
url = 'https://hq.gucheng.com/HSinfo.html?en_hq_type_code=&sort_field_name=px_change_rate&sort_type=desc&page=%s' # 股城网>行情>沪深A股
kline_filepath = 'F:\\files\\sharesDatas\\dayline\\' # 定义数据文件保存路径
codelist_filepath = 'F:\\files\\sharesDatas\\code_list\\' # 定义数据文件保存路径
controller = Controller(url=url, kline_filepath=kline_filepath, codelist_filepath=codelist_filepath, date='20190428')
controller.start()
https://github.com/CatsJuice/netease-stock-day-line)
或者:
git clone https://github.com/CatsJuice/netease-stock-day-line.git
3. 网易财经财务数据
http://quotes.money.163.com/f10/zycwzb_601318.html#01c01
可以看到同交易日线数据一样,这里有一个下载数据的按钮,对应盈利能力,偿还能力等,而这里直接通过开发者工具审查元素,即可看到超链接的href:/service/zycwzb_601318.html?type=report&part=ylnl,很快,就能拿到完整的url,然后操作同网易日线数据,项目地址同样位于:
1. 股票信息列表
http://data.eastmoney.com/zjlx/detail.html
2. 交易数据, 财务数据
东方财富的交易数据/财务数据我也尝试过使用爬虫爬取,但是还是要走不少弯路的,而且可能最后还没成功,首先,东方财富的数据页面和其他平台一样, 股票代码在url中,如http://data.eastmoney.com/bbsj/yjbb/600175.html, 直接爬取, 或者使用开发者工具定位页面元素会发现:
对应的数据是乱码的,而如果继续挖掘其js文件,是可以找到有加密的函数的,如下图所示:
在这个名字特别明显直白的js文件load_table_data_pc.js?201606021831中,可以看到加密,解密的方法,这也使得爬取成为可能,但是太大费周章暂不考虑。
篇幅有限,未完待续。
更多精彩,敬请关注~
搜索你感兴趣的内容吧
统计计量丨陈强:应用计量经济学的常见问题
软件应用丨吐血推荐,B站最强学习资源汇总(数据科学,机器学习,python)
软件应用丨Markdown(四):让你的文章“有声有色”
数据Seminar
这里是大数据、分析技术与学术研究的三叉路口
欢迎扫描👇二维码添加关注