为了账号安全,请及时绑定邮箱和手机立即绑定

Python进阶量化交易专栏场外篇20-爬虫抓取股票论坛帖子

欢迎大家订阅《教你用 Python 进阶量化交易》专栏!为了能够提供给大家更轻松的学习过程,笔者在专栏内容之外已陆续推出一些手记来辅助同学们学习本专栏内容,目前推出的扩展篇链接如下:

为了将专栏中分散的知识点贯穿起来,笔者在专栏的末尾小节《制作自己的量化交易工具》中分享了早期制作的一个简易版量化交易小工具,希望大家能够通过调试代码的方式掌握相关的知识。
目前在场外篇第9篇中已经移植到了Python3.7x版本上,接下来我们在这个版本的基础上逐步完善这个工具,使专栏的读者不仅能够通过小工具掌握专栏的相关知识点,也能够把工具用到自己的股票量化交易中去。

量化交易策略的研究主要涵盖了微观和宏观这两个方面,微观方面更多地是从市场价格和成交持仓这些基础信息为研究对象,通过算法计算出技术指标,再从技术指标的变化上构建交易模型。宏观方面则是基于更多的市场资讯开发交易模型,比如从CPI、PPI、货币发行量这些宏观经济指标为研究对象构建交易模型;或者是利用数据挖掘技术从新闻事件中挖掘出可能造成市场异常波动的事件,从而获得交易的时机。

本篇手记我们先梳理下能够获取到股票资讯的网站,然后再介绍下如何将爬取到的股票资讯在我们的GUI工具上显示。

  • 证券官方网站有上交所、深交所、证监会、证券业协会;
  • 行业信息网站有慧聪、阿里、万德;
  • 知名股票论坛:点金投资家园、股天下、MACD股市、东方财富网股吧、和讯股吧、创幻论坛。

接下来我们用爬虫抓取东方财富网股票论坛的帖子内容。

先通过浏览器访问论坛的URL:http://guba.eastmoney.com/list,002372.html,网页内容如下图所示:

图片描述

当我们点击第2页、第3页后,我们查看下URL为:http://guba.eastmoney.com/list,002372_2.html,http://guba.eastmoney.com/list,002372_3.html,因此得到了论坛网址的规律为 http://guba.eastmoney.com/list, 002372_%d.html 形式表示,其中的规律比较直白,%d为论坛第几页,不过这个形式是按评论时间排列的网址,如果按发帖时间的排列网址是http://guba.eastmoney.com/list,002372,f_%d.html。

读取网页内容的关键代码如下:

html_cont=request.urlopen(page_url).read()

需要注意的是Python2中有urllib、urllib2和urlparse,但在Python3中这些全部都被整合到了urllib中,其中Python2的urllib和urllib2中的内容整合为urllib.request模块,urlparse整合为urllib.parse模块。Python3中urllib中还包括response、error和robotparse等各种子模块。

获取到HTML代码部分内容如下:

图片描述

获取到HTML代码后,开始解析HTML代码。帖子由两部分组成,一部分为“财经评论”或“东方财富网”发布的公告或官方消息,另一部分为散户发布的讨论帖子,如下所示:

图片描述

前者的帖子的网址为http://guba.eastmoney.com/news,cjpl,902659513.html,后者帖子的网址为http://guba.eastmoney.com/news,002372,902629178.html,网址都可在HTML文件内容中搜寻到,如下所示:

图片描述

因此“财经评论”、“东方财富网”或者散户发布的帖子,主要的特征为/news,我们通过正则表达式来删选,实现代码如下,

pattern = re.compile('/news\S+html',re.S)
news_comment_urls = re.findall(pattern, html_cont.decode('utf-8')) # 非空白字符N次

"""
['/news,cjpl,902659513.html', '/news,cjpl,902684967.html', 
'/news,cjpl,902602349.html', '/news,cjpl,902529812.html', 
'/news,cjpl,857016161.html', '/news,002372,902629178.html', 
'/news,002372,902557935.html', '/news,002372,902533930.html', 
'/news,002372,902519348.html', '/news,002372,902468635.html', 
'/news,002372,902466626.html', '/news,002372,902464127.html', 
 ......
'/news,002372,901168702.html', '/news,002372,901153848.html']
"""

其中正则表达式的\S+表示匹配多次非空白字符,然后使用findall函数找到匹配的所有字符串,并把它们作为一个列表返回。

然后是使用urljoin方法把整个url拼接好用于爬取单个帖子的标题内容,代码如下所示:

for comment_url in news_comment_urls :
     whole_url = parse.urljoin(page_url, comment_url)
     post_urls.add(whole_url)
return post_urls

单个帖子爬取的内容包括三部分,帖子发表时间、作者及帖子标题,如下所示:

图片描述

图片描述

我们可以通过正则表达式进行提取,其中在组合正则表达式时,需要考虑到HTML代码中是否有重复的匹配关键字。作者和帖子标题正则代码如下,mainbody、zwcontentmain这些关键字在文本中仅出现一次,匹配程度较高。

com_cont = re.compile(r'<div id="mainbody">.*?zwconttbn.*?<a.*?<font>(.*?)</font>.*?<div.*?class="zwcontentmain.*?">.*?"zwconttbt">(.*?)</div>.*?social clearfix',re.DOTALL)

发布时间正则代码如下,分两步逐渐明晰的去提取时间,由于search是扫描字符串找到这个 RE 匹配的位置,因此增加group()返回匹配字符串。

pub_elems = re.search('<div class="zwfbtime">.*?</div>',html_cont2).group()
#<div class="zwfbtime">发表于 2020-02-11 09:54:48 东方财富Android版</div>
pub_time = re.search('\d\d\d\d-\d\d-\d\d',pub_elems).group()
#2020-02-06

另外,论坛帖子与当前的股价走势有时间联系,太早的帖子对现在无参考作用,因此需要删选近期的帖子。我们可以对时间进行判断,只有一个月之内发布的帖子才进行爬取并存储。

获取今天的日期使用datetime.now().date(),然后与爬取的帖子时间日期比较,timedelta可在日期上做天days时间计算,但需要将时间转换为时间形式,代码如下所示:

time_start=datetime.now().date() # 获取日期信息
dt = datetime.strptime(pub_time,"%Y-%m-%d") # 字符串转时间格式
datetime.date(dt)+timedelta(days=30)

完成了单个帖子的内容爬取之后,我们采用迭代方法把全部帖子爬取一遍。我们通过两层迭代方法,第一层为页数,第二层为一页的论坛帖子数,将每个帖子的网址存储在列表中,通过迭代的方式一个个爬取帖子的内容。

当我们爬取时发现某个帖子不存在,出现爬取信息异常时,可使用try…except…进行异常处理。

爬取到的帖子内容如下图所示:

图片描述

我们可以将爬取信息写到txt文件中。当然,我们也可以把爬取信息写到GUI工具中,我们知道在wxPython中文本框为wx.TextCtrl类,该类可以显示和编辑文本,这样一来就可以把帖子信息写到GUI工具中。显示效果如下所示:

图片描述

到这一步我们就得到论坛帖子中的各种内容,那么这些内容对于我们来说有什么意义吗?我们发现总有些人一直在唱空,制造恐慌的情绪,不过我们可以通过分类分析下这些空头评论和股价的涨跌有没有什么关联,是不是有写ID是专门来唱空的呢?感兴趣的朋友们可以试试!

关于完整代码可以加入专栏交流群获取。更多的量化交易内容欢迎大家订阅专栏阅读!!

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Python工程师
手记
粉丝
1.8万
获赞与收藏
1563

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消