查看原文
其他

软件应用丨Python量化投资学习报告之一:数据抓取

小徐 数据Seminar 2021-06-04




通过第三方数据平台直接调用api


一、TuShare(挖地兔)


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/

使用:

以历史行情接口为例:
http://tushare.org/trading.html#id2
如下调用:
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

通过切换页面可以发现,地址栏的url的参数发生了变化,变化的规律为:p=n, n为当前页码,而股票代码和股票名称,通过F12打开开发者工具,表单中的值均可定位到,应该可以通过爬虫抓取所有股票的基本信息,通过新浪财经抓取股票的代码我这里没有写,暂先略过。

2. 获取日线数据

在新浪财经数据中心, 我并没有找到交易数据的表格, 要查看某只股票的日线 / 分线只能通过点击某只股票进到详情页:

https://finance.sina.com.cn/realstock/company/sh601789/nc.shtml

(而url中的股票代码对应着不同的股票),而新浪财经的数据并非以表格的形式展示,而是通过图表展示,鼠标移动时通过JavaScript更新日期及当前股票的数据信息;那么直接抓取网页是无法获取到数据的,现在的思路是分析js文件,找到鼠标移动时监听到的事件,查明js是如何更新数据的,js的数据从哪里提取,以此来抓取信息;

首先,通过页面元素审查可以发现,分时线是通过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

这应该是爬虫抓取中喜闻乐见的格式了,换页不需要通过ajax,所以抓取的时候只需要设定好抓取的总页数,循环抓取页面再解析即可,接下来即可直接进行代码的编写(未亲自验证)。


二、网易财经


1. 获取所有股票代码(这里只考虑沪深A股)

网易财经的所有沪深A股数据位于:

http://quotes.money.163.com/old/#query=EQA&DataType=HS_RANK&sort=PERCENT&order=desc&count=24&page=0

从url来看,换页通过url传参即可改变了,但是实际操作可以发现,点击换页时url中的page并不会改变,但改变url中的page参数,当前页面序号会改变;但这不意味着可以像1.2.1. 新浪财经中爬取新浪财经一样,枚举url的page参数来爬取所有信息;因为股票数据是异步加载的,直接抓取无法获取到值;

对于网易财经,由于点击换页时页面的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,关于字段名的解释,以下为我的分析:

所以可以直接抓取这个url来获取相关的数据,更有趣的是,请求参数中有个count参数,决定了数据的数量,所以我尝试将count设置成全部数量,查看网易财经沪深A股,网易的编号最后一只为3607,所以如下请求:

'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 urllibimport jsonimport csvfrom 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

需要将url中的601318替换成相应的股票代码,在这个页面没有换页按钮,仅显示若干条数据,但是在数据表的右上角有个下载数据的链接,点击后,需要勾选需要下载的字段,点击下载后会下载一个code.csv文件,所以要做的就是抓取下载的真实url,按F12打开开发者工具,点击下载按钮后,在控制台看到如下提示:

其中已经包含了请求的真实地址,即:

"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 randomimport time
from stock.classes.Downloader import Downloaderfrom stock.classes.Parser import Parserfrom 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. 网易财经财务数据

网页url为:

http://quotes.money.163.com/f10/zycwzb_601318.html#01c01

可以看到同交易日线数据一样,这里有一个下载数据的按钮,对应盈利能力,偿还能力等,而这里直接通过开发者工具审查元素,即可看到超链接的href:/service/zycwzb_601318.html?type=report&part=ylnl,很快,就能拿到完整的url,然后操作同网易日线数据,项目地址同样位于:

https://github.com/CatsJuice/netease-stock-day-line)


三、东方财富


1. 股票信息列表

因为没找到合适的页面,实时资金流向排行:

http://data.eastmoney.com/zjlx/detail.html

爬取所有沪深A股代码, 这里不作详述。

2. 交易数据, 财务数据

东方财富的交易数据/财务数据我也尝试过使用爬虫爬取,但是还是要走不少弯路的,而且可能最后还没成功,首先,东方财富的数据页面和其他平台一样, 股票代码在url中,如http://data.eastmoney.com/bbsj/yjbb/600175.html, 直接爬取, 或者使用开发者工具定位页面元素会发现:

对应的数据是乱码的,而如果继续挖掘其js文件,是可以找到有加密的函数的,如下图所示:

在这个名字特别明显直白的js文件load_table_data_pc.js?201606021831中,可以看到加密,解密的方法,这也使得爬取成为可能,但是太大费周章暂不考虑。





篇幅有限,未完待续。

更多精彩,敬请关注~




今日推荐





搜索你感兴趣的内容吧


统计计量丨陈强:应用计量经济学的常见问题


软件应用丨吐血推荐,B站最强学习资源汇总(数据科学,机器学习,python)


软件应用丨Markdown(四):让你的文章“有声有色”





数据Seminar




这里是大数据、分析技术与学术研究的三叉路口



作者:小徐(徐志剑)排版编辑:青酱




    欢迎扫描👇二维码添加关注    


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存