Crossin的编程教室
标题: 用程序帮你炒股 [打印本页]
作者: crossin先生 时间: 2015-6-7 12:19
标题: 用程序帮你炒股
最近在知乎上看到一个问题:如何使用 Python 抓取雪球网页?
雪球是国内一个人气很高的股票财经类网站,上面有个投资组合功能,很多民间股神在上面设定自己的投资组合,收益率百分之几百的一大把。题主就问,怎么能通过程序来跟踪一个组合的持仓变化,有变动的时候就自动提示。
这个问题可能提的有段时间了,因为看回答里说,现在关注一个组合,就会有持仓变动的提示了。不过我觉得这事情挺有意思的。比如可以把很多持仓的数据都抓下来,做一些综合的分析,看看现在网站上被持有最多的股票是哪一支,某一天被调入最多的又是哪一支之类。
于是我决定来抓抓看,顺便借此说说我通常用程序做自动抓取的过程。这里只说个大概思路和部分代码片段,具体代码可以去 Github 上下载。
Step.1 分析页面
要抓一个网页,首先自然是要“研究”这个网页。通常我会用两种方式:
一个是 Chrome 的 Developer Tools。通过它里面的 Network 功能可以看到页面发出的所有网络请求,而大多数数据请求都会在 XHR 标签下。点击某一个请求,可以看到其具体信息,以及服务器的返回结果。很多网站在对于某些数据会有专门的请求接口,返回一组 json 或者 XML 格式的数据,供前台处理后显示。
另一个就是直接查看网页源代码。通常浏览器的右键菜单里都有这个功能。从页面的 HTML 源码里直接寻找你要的数据,分析它格式,为抓取做准备。
SNB.cubeInfo = {"id":10289,"name":"誓把老刀挑下位","symbol":"ZH010389" ...此处略过三千字... "created_date":"2014.11.25"}
SNB.cubePieData = [{"name":"汽车","weight":100,"color":"#537299"}];
cubeInfo 是一个 json 格式的数据,看上去就是我们需要的内容。一般我会找个格式化 json 的网站把数据复制进去方便查看。
这应该就是组合的持仓数据。那么接下来,一切似乎都简单了。只要直接发送网页请求,然后把其中 cubeInfo 这段文字取出,按 json 读出数据,就完成了抓取。甚至不用动用什么 BeautifulSoup、正则表达式。
Step.2 获取页面
分析完毕,开抓。
直接 urllib.urlopen 向目标网页发送请求,读出网页。结果,失败了……
看了下返回结果:
403 Forbidden
You don't have permission to access the URL on this server. Sorry for the inconvenience.
被拒了,所以这种赤裸裸地请求是不行的。没关系,那就稍微包装一下:
send_headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Connection':'keep-alive',
'Host':'xueqiu.com',
'Cookie':r'xxxxxx',
}
req = urllib2.Request(url, headers=send_headers)
resp = urllib2.urlopen(req)
html = resp.read()
header 数据都可以从 Developer Tools 里拿到。这次顺利抓到页面内容。
一般网站或多或少都会对请求来源做一些阻拦,通过加 header 可以搞定大部分情况。
Step.3 提取数据
因为这个数据比较明显,直接用通过一些字符串查找和截取操作就可以取出来。
pos_start = html.find('SNB.cubeInfo = ') + len('SNB.cubeInfo = ')
pos_end = html.find('SNB.cubePieData')
data = html[pos_start:pos_end]
dic = json.loads(data)
dic 就是一个包含数据的字典对象。之后想干什么就随便你了。
对于复杂一点的情况,可以通过 BeautifulSoup 来定位 html 标签。再不好办的,就用正则表达式,基本都可以解决掉。
Step.4 处理数据
因为我想对数据进行持久化存储,并且做展示和分析,所以我用了 django 里的 ORM 来处理抓下来的数据。
# add Portfolio
portfolio, c = models.Portfolio.objects.get_or_create(code=dic['symbol'])
portfolio.name = dic['name']
portfolio.earnings = dic['total_gain']
portfolio.save()
# add Stock
stocks = dic['view_rebalancing']['holdings']
for s in stocks:
stock, c = models.Stock.objects.get_or_create(code=s['stock_symbol'])
stock.name = s['stock_name']
stock.count += 1
stock.weight += s['weight']
stock.save()
Portfolio 记录下组合及其收益,Stock则记录每支股票的被收录数和总收录份额。
对于抓取到的,一般也可以存在文件中,或者直接通过 SQL 存入数据库,视不同情况和个人喜好而定。
Step.5 批量抓取
前面的一套做下来,就完整地抓取了一组数据。要达到目的,还要设计一下批量抓取的程序。
一个要解决的问题就是如何获得组合列表。这个可以再通过另一个抓取程序来实现。然后根据这些列表来循环抓取就可以了。
若要细究,还要考虑列表如何保存和使用,如何处理抓取失败和重复抓取,如何控制抓取频率防止被封,可否并行抓取等等。
Step.6 数据分析
数据有了,你要怎么用它,这是个很大的问题。可以简单的统计现象,也可以想办法深入分析背后隐藏的逻辑。不多说,我也还只是在摸索之中。
经常有人问我,学了基础之后要如何进阶?我的回答是,多看代码,多写代码,找些项目练手。然后对方很可能回追问,到哪里找练手的项目?
我想说的是,处在现在这个互联网爆炸的时代,身边到处都是项目。我会用程序批量处理文件、定时查火车出票,或者像本文这种信息抓来看看。现在很多人想方设法把东西往互联网上搬,水果、打车、按摩师全都上了网。对于一个会写程序的人来说,还会觉得没有事情可做吗?
另,抓取的代码也放在了我的 Github 上:
作者: Tig 时间: 2015-6-8 00:20
本帖最后由 Tig 于 2015-6-8 00:23 编辑
正好这两天也做了这个。。
抓取雪球网的热门组合,统计最热股票,然后通过先生的web.py教程,做了个小网站
还是要多练练手。。做起来才能感觉到哪些基础知识还需要补习
用了BS4来获取文本0 0。。还真是挺费劲的
作者: book 时间: 2015-6-9 21:13
本帖最后由 book 于 2015-6-9 22:54 编辑
老师您好:
我下载了您的代码,
portfolios = models.Portfolio.objects.all()
for p in portfolios:
print p.id, p.name
fetch_portfolio(p.code)
time.sleep(1)
运行这一段时,出现“OperationalError: no such table: snowball_portfolio”,不知道是何原因,请指教。
作者: crossin先生 时间: 2015-6-10 10:29
book 发表于 2015-6-9 21:13
老师您好:
我下载了您的代码,
portfolios = models.Portfolio.objects.all()
说明一下:
要直接跑这个代码,首先要装django环境
其次要初始化models,即要 ./manage.py migrate
作者: book 时间: 2015-6-10 10:39
crossin先生 发表于 2015-6-10 10:29
说明一下:
要直接跑这个代码,首先要装django环境
其次要初始化models,即要 ./manage.py migrate ...
老师您好,我是在win环境下安装了django环境,不是在linux下运行,请问有没有什么解决方法?
作者: crossin先生 时间: 2015-6-10 15:28
book 发表于 2015-6-10 10:39
老师您好,我是在win环境下安装了django环境,不是在linux下运行,请问有没有什么解决方法? ...
win、linux过程都一样的
你这个应该是建立数据库的过程出错了
如果你只是练习抓取的话,建议把数据存储那一部分先去掉,保存到文件里
如果要用django,可以先看下它的quick start,如何初始化并建立数据库
作者: book 时间: 2015-6-10 21:37
crossin先生 发表于 2015-6-10 15:28
win、linux过程都一样的
你这个应该是建立数据库的过程出错了
如果你只是练习抓取的话,建议把数据存储那 ...
谢谢老师,我创建表了。但是头包装时,每次运行,每次要更换cookie,不更换就是会出现
“HTTPError: HTTP Error 400: Bad Request”,这个有没有更好的包装办法?
作者: crossin先生 时间: 2015-6-11 11:00
book 发表于 2015-6-10 21:37
谢谢老师,我创建表了。但是头包装时,每次运行,每次要更换cookie,不更换就是会出现
“HTTPError: HTTP ...
这我倒没有遇到过,一次cookie应该可以用很久的。我是拿未登录状态下的cookie来用的。
不知道是不是在哪里引发了cookie的变动?
作者: jianghu52 时间: 2015-6-11 20:15
我用的是request包。感觉比urllib2要简单一些。- headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36'}
- url = 'http://xueqiu.com/p/ZH297574'
- x = requests.get(url,headers=headers)
- html = x.text
- pos_start = html.find('SNB.cubeInfo = ') + 15
- pos_end = html.find('SNB.cubePieData')
- data = html[pos_start:pos_end]
- dic = json.loads(data)
复制代码 但是我现在遇到的一个问题是python2里面的unicode的问题。
我直接printdic['view_rebalancing']['holdings'][0]的时候,得到的是
{u'stock_id': 1008310, 。。。 u'segment_name': u'\u533b\u836f\u533b\u7597', u'stock_symbol': u'AKBA', u'proactive': False}
如果我直接打
dic['view_rebalancing']['holdings'][0]['segment_name']
出来的就是 ‘医药医疗’
用的都是print方法。这个要怎么解决啊。
作者: crossin先生 时间: 2015-6-12 11:28
jianghu52 发表于 2015-6-11 20:15
我用的是request包。感觉比urllib2要简单一些。但是我现在遇到的一个问题是python2里面的unicode的问题。
...
你只能把dict对象的某项内容取出来再print才能解码
整体print是不会对其中的文字解码的
欢迎光临 Crossin的编程教室 (https://bbs.crossincode.com/) |
Powered by Discuz! X2.5 |