用程序帮你炒股

最近在知乎上看到一个问题:如何使用 Python 抓取雪球网页?

雪球是国内一个人气很高的股票财经类网站,上面有个投资组合功能,很多民间股神在上面设定自己的投资组合,收益率百分之几百的一大把。题主就问,怎么能通过程序来跟踪一个组合的持仓变化,有变动的时候就自动提示。

这个问题可能提的有段时间了,因为看回答里说,现在关注一个组合,就会有持仓变动的提示了。不过我觉得这事情挺有意思的。比如可以把很多持仓的数据都抓下来,做一些综合的分析,看看现在网站上被持有最多的股票是哪一支,某一天被调入最多的又是哪一支之类。

于是我决定来抓抓看,顺便借此说说我通常用程序做自动抓取的过程。这里只说个大概思路和部分代码片段,具体代码可以去 Github 上下载。

Step.1 分析页面

要抓一个网页,首先自然是要“研究”这个网页。通常我会用两种方式:

一个是 Chrome 的 Developer Tools。通过它里面的 Network 功能可以看到页面发出的所有网络请求,而大多数数据请求都会在 XHR 标签下。点击某一个请求,可以看到其具体信息,以及服务器的返回结果。很多网站在对于某些数据会有专门的请求接口,返回一组 json 或者 XML 格式的数据,供前台处理后显示。

另一个就是直接查看网页源代码。通常浏览器的右键菜单里都有这个功能。从页面的 HTML 源码里直接寻找你要的数据,分析它格式,为抓取做准备。

对于雪球上的一个组合页面 http://xueqiu.com/P/ZH010389,粗略地看了一下它发出的请求,并没有如预想那样直接找到某个数据接口。看源代码,发现有这样一段:

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 上:

github.com/crossin/avalanche

原文发布于微信公众号 - Crossin的编程教室(crossincode)

原文发表时间:2015-06-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏牛客网

腾讯一面应用开发

最遗憾的,叫写冒泡排序都能写数组溢出,非科班面对算法题真的紧张。凉凉。 面试官是做php的,我用java。 问了http和https的区别。 答: 后者是前者的...

30460
来自专栏JackieZheng

解决Myeclipse下Debug出现Source not found以及sql server中导入数据报错

前言:在空间里回顾了我的2014,从生活、技术、家庭等各方面对自己进行总结剖析,也是给自己一个交代。也想在博客上专门写一篇2014年度菜鸟的技术路回忆录,但是...

33950
来自专栏web前端教室

我建议这样思路写组件

我建议这样思路写组件:首先一个大函数就是一个模块,可以简单到只是用function写,通过config对象写配置。然后通过传参调用,把这些模块连接起来,能实现功...

19750
来自专栏EAWorld

生成全局唯一ID的3个思路,来自一个资深架构师的总结

标识(ID / Identifier)是无处不在的,生成标识的主体是人,那么它就是一个命名过程,如果是计算机,那么它就是一个生成过程。如何保证分布式系统下,并行...

73060
来自专栏程序生活

Python爬虫系列(一)初期学习爬虫的拾遗与总结(11.4更)

---- 最近,为了提取裁判文书网的有关信息,自己迈入Python的学习之路,写了快两周的代码,自己写这篇文章总结下踩过的坑,还有遇到一些好的资料和博客等总结下...

41950
来自专栏搞前端的李蚊子

IOS safari浏览器登陆时Cookie无法保存的问题

近期完成了一个儿童的测评项目,测试到最后的时候发现在ipad mini上登陆成功之后无法跳转页面,而安卓和pc端都可以,找了大半天bug,发现其他的苹果设备都没...

1K50
来自专栏腾讯Bugly的专栏

精神哥讲Crash(二): java.lang.NoSuchMethodError

圣诞节到啦,精神哥披着红大衣,戴上红高帽,想着偷偷爬入各位办公楼的烟囱,给大家的座位上留下一包鸡爪作为圣诞礼物! 奈何精神哥太胖,肚子太大爬不进烟囱了,想着大家...

38550
来自专栏hbbliyong

设计模式学习--面向对象的5条设计原则之开放封闭原则--OCP

一、OCP简介(OCP--Open-Closed Principle): Software entities(classes,modules,functions...

30580
来自专栏Crossin的编程教室

【Python 第2课】print

今天早上醒来,发现咱们的同学人数一夜之间多了50,后来又陆陆续续来了很多,于是我坚持下去的信心又增加了不少。在这里感谢连客官微的宣传,表示今晚将用加班写代码来表...

29270
来自专栏web前端教室

什么生命周期,在我看来就是各种回调 &&电商项目作业检查 -- 张xx

今天咱们的零基础课讲到了react的生命周期,什么三种状态啊,五种处理事件啊,函数的名字都特别的长。还有表单应用和react中哪使用ajax方法,以及get回来...

18580

扫码关注云+社区

领取腾讯云代金券