社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python爬虫股票评论,snowNLP简单分析股民用户情绪

大数据挖掘DT数据分析 • 6 年前 • 697 次点击  

 向AI转型的程序员都关注了这个号👇👇👇


大数据挖掘DT数据分析  公众号: datadw



一、背景

    股民是网络用户的一大群体,他们的网络情绪在一定程度上反映了该股票的情况,也反映了股市市场的波动情况。作为一只时间充裕的研究僧,我课余时间准备写个小代码get一下股民的评论数据,分析以下用户情绪的走势。代码还会修改,因为结果不准确,哈哈!


二、数据来源

    本次项目不用于商用,数据来源于东方财富网,由于物理条件,我只获取了一只股票的部分评论,没有爬取官方的帖子,都是获取的散户的评论。


三、数据获取

Python是个好工具,这次我使用了selenium和PhantomJS组合进行爬取网页数据,当然还是要分析网页的dom结构拿到自己需要的数据。


爬虫部分:


from selenium import webdriver
import time
import json
import re
# from HTMLParser import HTMLParser  
from myNLP import *  
# from lxml import html  
# import requests  
class Crawler:  
   url = ''  
   newurl = set()
headers = {}
cookies = {}
def __init__(self, stocknum, page):  
       self.url = 'http://guba.eastmoney.com/list,'+stocknum+',5_'+page+'.html'  
       cap = webdriver.DesiredCapabilities.PHANTOMJS
cap["phantomjs.page.settings.resourceTimeout"] = 1000  
       #cap["phantomjs.page.settings.loadImages"] = False  
       #cap["phantomjs.page.settings.localToRemoteUrlAccessEnabled"] = True  
       self.driver = webdriver.PhantomJS(desired_capabilities=cap)
def crawAllHtml(self,url):  
       self.driver.get(url)
time.sleep(2)
#         htmlData = requests.get(url).content.decode('utf-8')  
#         domTree = html.fromstring(htmlData)  
#         return domTree  
   def getNewUrl(self,url):  
       self.newurl.add(url)
def filterHtmlTag(self, htmlStr):  
       self.htmlStr = htmlStr    
       #先过滤CDATA    
       re_cdata=re.compile('//
[>]//
>',re.I) # 匹配CDATA    
       re_script=re.compile(']*>[^',re.I)#Script    
       re_style=re.compile(']*>[^',re.I)#style    
       re_br=re.compile('
'
)#处理换行    
       re_h=re.compile('?\w+[^>]*>')#HTML标签    
       re_comment=re.compile('')#HTML注释    
       s=re_cdata.sub('',htmlStr)#去掉CDATA    
       s=re_script.sub('',s) #去掉SCRIPT    
       s=re_style.sub('',s)#去掉style    
       s=re_br.sub('\n',s)#br转换为换行    
       blank_line=re.compile('\n+')#去掉多余的空行    
       s = blank_line.sub('\n',s)
s=re_h.sub('',s) #去掉HTML 标签    
       s=re_comment.sub('',s)#去掉HTML注释    
       #去掉多余的空行    
       blank_line=re.compile('\n+')
s=blank_line.sub('\n',s)
return s
def getData(self):  
       comments = []
self.crawAllHtml(self.url)
postlist = self.driver.find_elements_by_xpath('//*[@id="articlelistnew"]/div')
for post in postlist:  
           href = post.find_elements_by_tag_name('span')[2].find_elements_by_tag_name('a')
if len(href):  
               self.getNewUrl(href[0].get_attribute('href'))
#             if len(post.find_elements_by_xpath('./span[3]/a/@href')):  
#                 self.getNewUrl('http://guba.eastmoney.com'+post.find_elements_by_xpath('./span[3]/a/@href')[0])  
       for url in self.newurl:  
           self.crawAllHtml(url)
time = self.driver.find_elements_by_xpath('//*[@id="zwconttb"]/div[2]')
post = self.driver.find_elements_by_xpath('//*[@id="zwconbody"]/div')
age = self.driver.find_elements_by_xpath('//*[@id="zwconttbn"]/span/span[2]')
if len(post) and len(time) and len(age):  
               text = self.filterHtmlTag(post[0].text)
if len(text):  
                   tmp = myNLP(text)
comments.append({'time':time[0].text,'content':tmp.prob, 'age':age[0].text})
commentlist = self.driver.find_elements_by_xpath('//*[@id="zwlist"]/div')
if len(commentlist):  
               for comment in commentlist:  
                   time = comment.find_elements_by_xpath('./div[3]/div[1]/div[2]')
post = comment.find_elements_by_xpath('./div[3]/div[1]/div[3]')
age = comment.find_elements_by_xpath('./div[3]/div[1]/div[1]/span[2]/span[2]')
if len(post) and len(time) and len(age):  
                       text = self.filterHtmlTag(post[0].text)
if len(text):  
                           tmp = myNLP(text)
comments.append({'time':time[0].text,'content':tmp.prob, 'age':age[0].text})
return json.dumps(comments)


存储部分:

这部分其实可以用数据库来做,但是由于只是试水,就简单用json文件来存部分数据
import io  
class File:  
   name = ''  
   type = ''  
   src = ''  
   file = ''  
   def __init__(self,name, type, src):  
       self.name = name  
       self.type = type  
       self.src = src    
       filename = self.src+self.name+'.'+self.type
self.file = io.open(filename,'w+', encoding = 'utf-8')
def inputData(self,data):  
       self.file.write(data.decode('utf-8'))
self.file.close()
def closeFile(self):  
       self.file.close()


测试用的local服务器:

这里只是为了要用浏览器浏览数据图,由于需要读取数据,js没有权限操作本地的文件,只能利用一个简单的服务器来弄了


  1. import SimpleHTTPServer  

  2. import SocketServer;  

  3. PORT = 8000  

  4. Handler = SimpleHTTPServer.SimpleHTTPRequestHandler  

  5. httpd = SocketServer.TCPServer(("", PORT), Handler);  

  6. httpd.serve_forever()  


NLP部分:snowNLP这个包还是用来评价买卖东西的评论比较准确

不是专门研究自然语言的,直接使用他人的算法库。这个snowNLP可以建立一个训练,有空自己来弄一个关于股票评论的。


  1. #!/usr/bin/env python  

  2. # -*- coding: UTF-8 -*-  

  3. from snownlp import SnowNLP  

  4. class myNLP:  

  5.     prob = 0.5  

  6.     def _init_(self, text):  

  7.         self.prob = SnowNLP(text).sentiments  


主调度:

  1. # -*- coding: UTF-8 -*-  

  2. ''''' 

  3. Created on 2017年5月17日 

  4. @author: luhaiya 

  5. @id: 2016110274 

  6. @description: 

  7. '''  

  8. #http://data.eastmoney.com/stockcomment/  所有股票的列表信息  

  9. #http://guba.eastmoney.com/list,600000,5.html 某只股票股民的帖子页面  

  10. #http://quote.eastmoney.com/sh600000.html?stype=stock 查询某只股票  

  11. from Crawler import *  

  12. from File import *  

  13. import sys  

  14. default_encoding = 'utf-8'  

  15. if sys.getdefaultencoding() != default_encoding:  

  16.     reload(sys)  

  17.     sys.setdefaultencoding(default_encoding)  

  18.              

  19. def main():  

  20.     stocknum = str(600000)  

  21.     total = dict()  

  22.     for i in range(1,10):  

  23.         page = str(i)  

  24.         crawler = Crawler(stocknum, page)  

  25.         datalist = crawler.getData()  

  26.         comments = File(stocknum+'_page_'+page,'json', './data/')  

  27.         comments.inputData(datalist)  

  28.         data = open('./data/'+stocknum+'_page_'+page+'.json','r').read()  

  29.         jsonData = json.loads(data)  

  30.         for detail in jsonData:  

  31.             num = '1' if '年' not in detail['age'].encode('utf-8'else detail['age'].encode('utf-8').replace('年','')  

  32.             num = float(num)  

  33.             date = detail['time'][4:14].encode('utf-8' )  

  34.             total[date] = total[date] if date in total.keys() else {'num':0'content':0}  

  35.             total[date]['num'] = total[date]['num'] + num if total[date]['num'else num  

  36.             total[date]['content'] = total[date]['content'] + detail['content']*num if total[date]['content'else detail['content']*num  

  37.     total = json.dumps(total)  

  38.     totalfile = File(stocknum,'json','./data/')  

  39.     totalfile.inputData(total)  

  40. if __name__ == "__main__":  

  41.     main()  


四、前端数据展示

使用百度的echarts。用户的情绪是使用当天所有评论的情绪值的加权平均,加权系数与用户的股龄正相关。


  1. html>  

  2. <html>  

  3. <head>  

  4. <meta charset="UTF-8">  

  5. <title>分析图表title>  

  6. <style>  

  7. body{texr-align:center;}  

  8. #mainContainer{width:100%;}  

  9. #fileContainer{width:100%; text-align:center;}  

  10. #picContainer{width: 800px;height:600px;margin:0 auto;}  

  11. style>  

  12. head>   

  13. <body>  

  14. <div id = 'mainContainer'>  

  15. <div id = 'fileContainer'>这里是文件夹列表div>  

  16. <div id = 'picContainer'>div>  

  17. div>  

  18. <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js">script>   

  19. <script src = "./echarts.js">script>  

  20. <script>  

  21. main();  

  22. function main(){  

  23.     var stocknum = 600000;  

  24.     getDate(stocknum);  

  25. }  

  26. function getDate(stocknum){  

  27.     var src = "./data/"+stocknum+".json";  

  28.     $.getJSON(src, function (res){  

  29.         var date = [];  

  30.         for(var key in res){  

  31.             key = key.replace('-','/').replace('-','/');  

  32.             date.push(key);  

  33.         }  

  34.         date.sort();  

  35.         data = [];  

  36.         for (var i = 0; i < date.length; i++) {  

  37.             dat  = date[i].replace('/','-').replace('/','-');  

  38.             data.push(res[dat]['content']/res[dat]['num']);  

  39.         }  

  40.         drawPic(date,data);  

  41.     })  

  42. }  

  43. function drawPic(date, data){  

  44.     //initialize and setting options  

  45.     var myChart = echarts.init(document.getElementById('picContainer'));  

  46.     option = {  

  47.         tooltip: {  

  48.             trigger: 'axis',  

  49.             position: function (pt) {  

  50.                 return [pt[0], '10%'];  

  51.             }  

  52.         },  

  53.         title: {  

  54.             left: 'center',  

  55.             text: '股票情绪走向图',  

  56.         },  

  57.         toolbox: {  

  58.             feature: {  

  59.                 dataZoom: {  

  60.                     yAxisIndex: 'none'  

  61.                 },  

  62.                 restore: {},  

  63.                 saveAsImage: {}  

  64.             }  

  65.         },  

  66.         xAxis: {  

  67.             type: 'category',  

  68.             boundaryGap: false,  

  69.             data: date  

  70.         },  

  71.         yAxis: {  

  72.             type: 'value',  

  73.             boundaryGap: [0, '100%']  

  74.         },  

  75.         dataZoom: [{  

  76.             type: 'inside',  

  77.             start: 0,  

  78.             end: 10  

  79.         }, {  

  80.             start: 0,  

  81.             end: 10,  

  82.             handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',  

  83.             handleSize: '80%',  

  84.             handleStyle: {  

  85.                 color: '#fff',  

  86.                 shadowBlur: 3,  

  87.                 shadowColor: 'rgba(0, 0, 0, 0.6)',  

  88.                 shadowOffsetX: 2,  

  89.                 shadowOffsetY: 2  

  90.             }  

  91.         }],  

  92.         series: [  

  93.             {  

  94.                 name:'stocknum',  

  95.                 type:'line',  

  96.                 smooth:true,  

  97.                 symbol: 'none',  

  98.                 sampling: 'average',  

  99.                 itemStyle: {  

  100.                     normal: {  

  101.                         color: 'rgb(255, 70, 131)'  

  102.                     }  

  103.                 },  

  104.                 areaStyle: {  

  105.                     normal: {  

  106.                         color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{  

  107.                             offset: 0,  

  108.                             color: 'rgb(255, 158, 68)'  

  109.                         }, {  

  110.                             offset: 1,  

  111.                             color: 'rgb(255, 70, 131)'  

  112.                         }])  

  113.                     }  

  114.                 },  

  115.                 data: data  

  116.             }  

  117.         ]  

  118.     };  

  119.     //draw pic  

  120.     myChart.setOption(option);    

  121. }  

  122. script>  

  123. body>  

  124. html>  





图1


图2


图1是我分析用户情绪画出的时间推进图,理论上小于0.5表消极情绪,大于0.5表示积极情绪。图2是实际股价的走势。

via: http://blog.csdn.net/SeaIsGod/article/details/72859071


人工智能大数据与深度学习

搜索添加微信公众号:weic2c

长按图片,识别二维码,点关注



大数据挖掘DT数据分析

搜索添加微信公众号:datadw


教你机器学习,教你数据挖掘

长按图片,识别二维码,点关注






今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/COaVbVGVXL
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/4520
 
697 次点击