既然要用深度学习来做量化, 那么数据是必不可少的! 数据闭环也是必须的! 那么爬虫就非常重要了! 也许有人要质疑实时性对量化决策的影响, 对不起, 事实证明深度学习并不会产生高频交易! 另外说一点, 做量化的去年基本有一个共识就是只有高频交易能够赚钱, 事实上是大错特错的! 原因我后面的文章会讲到!
我们以抓取数字货币的 OHLCV 数据为例, 我们从 coinmarket 网站爬取我们所需要的数据! 首先我们要模拟一个浏览器操作, 否则会被禁! 如何做呢?
模拟一个送一个 header 就可以了! 当然我们还需要使用代理, 否则也是很容易被禁 IP 的! 我们可以从一些代理网站上定期去爬取一些 IP 地址来用! 接下来, 我们只要使用 request 这个包, 就可以抓取当前页面的内容, python 作为胶水语言还是非常称职的! 抓取到数据后, 我们讲数据解析出来就可以存成一个 JSON 文件备用了! 如下是部分的代码:
- from enum import Enum
- import os
- import JSON
- import datetime
- import http
- def parse_ohlcv(self, code, data):
- df = pd.DataFrame()
- date = {
- 'Jan':'01',
- 'Feb':'02',
- 'Mar':'03',
- 'Apr':'04',
- 'May':'05',
- 'Jun':'06',
- 'Jul':'07',
- 'Aug':'08',
- 'Sep':'09',
- 'Oct':'10',
- 'Nov':'11',
- 'Dec':'12'
- }
- q = re.compile(r's*(.*)s*(.*)s*(.*)s*(.*)s*(.*)s*.*s*.*s*')
- data = q.findall(data)
- for d1, d2, d3, d4, d5, d6, d7 in reversed(data):
- res = re.match(r'(D+)s+(d+),s+(d+)$', d1)
- d1 = res.group(3) + date[res.group(1)] + res.group(2)
- if d2 == '-' or d3 == '-' or d4 == '-' or d5 == '-' or d6 == '-' or d7 == '-':
- continue
- edf = pd.DataFrame({
- 'code': [code], 'date':[d1], 'open':['%.2f'%float(d2)], 'high':['%.2f'%float(d3)], 'low':['%.2f'%float(d4)], 'close':['%.2f'%float(d5)], 'volume':['%.2f'%float(d6)], 'amount':['%.2f'%float(0.0)]
- })
- df = df.append(edf)
- return df
- def get_ohlcv(self, code, start, end):
- url = 'https://coinmarketcap.com/currencies/{}/historical-data/?start={}&end= {}'.format(self.CryptoMapping.value[code].split(',')[1], start[:8], end[:8])
- while True:
- try:
- if hasattr(self, 'proxy') and not len(self.proxy) == 0:
- pindex = int(time.time()) % len(self.proxy)
- p, p2, p3 = self.proxy['proxy'][pindex].split(':')
- ip = p2 + ':' + p3
- handler = rqt.ProxyHandler({
- p:ip
- })
- else:
- handler = rqt.HTTPHandler()
- opener = rqt.build_opener(handler)
- opener.addheaders = self.get_header_for_opener_method()
- p = opener.open(url, timeout=20).read().decode('utf-8')
- opener.close()
- except err.HTTPError as e:
- print('http error:', e.code, e.reason)
- except err.URLError as e:
- print('url error:', e.errno, e.reason)
- except socket.timeout:
- print('timeout error')
- except (http.client.IncompleteRead, http.client.BadStatusLine, ConnectionResetError) as e:
- print('IncompleteRead')
- else:
- break
- df = parse_ohlcv(code, p)
- return df
我们来细讲一下代码:
先 import 一些我们需要的包:
- from enum import Enum
- import os
- import JSON
- import datetime
- import http
首先, 函数接受三个参数, 分别是数字货币的代码, 要抓取的 OHLCV 的起始日期和结束日期.
def get_ohlcv(self, code, start, end):
然后我们可以找到数据页面的网址, 可以用 fiddler 抓取到具体送出的 request!
url = 'https://coinmarketcap.com/currencies/{}/historical-data/?start={}&end= {}'.format(self.CryptoMapping.value[code].split(',')[1], start[:8], end[:8])
接下来, 我们查看我们的代理列表并从中随机取一个代理 IP. 用代理和不用代理是 request handler 是用的不一样的方法!
- while True:
- try:
- if hasattr(self, 'proxy') and not len(self.proxy) == 0:
- pindex = int(time.time()) % len(self.proxy)
- p, p2, p3 = self.proxy['proxy'][pindex].split(':')
- ip = p2 + ':' + p3
- handler = rqt.ProxyHandler({
- p:ip
- })
- else:
- handler = rqt.HTTPHandler()
最后就是使用 opener 来抓取数据了, 我们先要用如下方法去构造一个 header.
- def get_header_for_opener_method():
- user_agents = [
- 'Mozilla/5.0 (Windows NT 6.1; WOW64) ApplewebKit/537.36 (Khtml, like Gecko) Chrome/39.0.2171.71 Safari/537.36' 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36',
- 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393',
- 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)']
- rand_index = int(time.time()) % len(user_agents)
- header_tuple = [('User-Agent', user_agents[rand_index]),
- ('Referer', 'www.baidu.com'),
- ('Connection', 'keep-alive')]
- return header_tuple
然后我们再抓取数据的时候注意编码方式要根据实际情况使用'utf-8'或者'CP936'等. 有时候由于网络原因我们会出现 timeout 或者读取数据未完成的错误, 所以要抛出如下几个异常, 让程序继续去 try!
- opener = rqt.build_opener(handler)
- opener.addheaders = self.get_header_for_opener_method()
- p = opener.open(url, timeout=20).read().decode('utf-8')
- opener.close()
基本上这就是一个爬虫的最基础的部分. 接下来我们来讲一下如何来解析抓取下来的数据.
如果是 JSON 格式的数据, 处理起来就非常方便, 本次我们抓取到的数据是网页表格中的数据, 所以需要自己手动来解析.
首先, 我们实例一个 dataframe 用于存储我们解析好的数据.
- def parse_ohlcv(self, code, data):
- df = pd.DataFrame()
然后我们使用 re 模块来定一个正则表达式来匹配到我们需要的内容. 正则表达式我们后面可以开一个专题来讲讲. 这里我们只要知道 s * 匹配 0 个, 1 个或者多个空字符,.* 表示匹配任意字符串 () 表示模式内存, 也就是说 res.group 中会按顺序存储匹配到的内容.
- date = {
- 'Jan':'01',
- 'Feb':'02',
- 'Mar':'03',
- 'Apr':'04',
- 'May':'05',
- 'Jun':'06',
- 'Jul':'07',
- 'Aug':'08',
- 'Sep':'09',
- 'Oct':'10',
- 'Nov':'11',
- 'Dec':'12'
- }
- q = re.compile(r's*(.*)s*(.*)s*(.*)s*(.*)s*(.*)s*.*s*.*s*')
接下来开始匹配数据, 并对数据进行一些格式处理!
- data = q.findall(data)
- for d1, d2, d3, d4, d5, d6, d7 in reversed(data):
- res = re.match(r'(D+)s+(d+),s+(d+)$', d1)
接下来将数据存储到 dataframe 中:
- d1 = res.group(3) + date[res.group(1)] + res.group(2)
- if d2 == '-' or d3 == '-' or d4 == '-' or d5 == '-' or d6 == '-' or d7 == '-':
- continue
- edf = pd.DataFrame({
- 'code': [code], 'date':[d1], 'open':['%.2f'%float(d2)], 'high':['%.2f'%float(d3)], 'low':['%.2f'%float(d4)], 'close':['%.2f'%float(d5)], 'volume':['%.2f'%float(d6)], 'amount':['%.2f'%float(0.0)]
- })
- df = df.append(edf)
- return df
到这里, 基本上一个爬虫的爬取数据和解析就完成了, 当然其实解析 HTML 有好用的 python 包, 为了更好的呈现过程, 并没有使用! 原则上我们是一定要用的, 除非轮子确实不好用, 我们才自力更生!
了解更多技巧, 请移步我的星球: AI 量化(https://t.zsxq.com/RvfY37y) 星球限时免费, 如需加入, 请私信我获得免费邀请码!
零基础学习 Python 与深度学习应用请关注星球: Python 与深度学习 https://t.zsxq.com/bUFayZ3
微信公众号: QTechAI
来源: http://www.jianshu.com/p/62b25f483817