招商银行fintech选拔课题---《基于微博爬虫的舆情分析》上

    最近参加了招商银行总行的fintench精英技术训练营的选拔赛,在通过笔试后,进入了课题研究的环节。因为前段时间学习了一段时间Python,所以选择了《基于微博爬虫的舆情分析》这一课题。该课题的具体要求如下:

课题背景: 请设计微博爬虫,获取微博上最近N天(N<=10)内与招商银行相关的热点新闻与用户意见,代码可根据输入的天数返回最新的微博信息。在已收集的数据中对提及的重点内容(招行相关产品、服务和重点事件等)进行抽取并进行一定程度上的情绪判定。最终以浅显易懂的方式呈现该段时间范围内微博上与招行相关的舆情信息,其中呈现的具体内容和方式可由考生自行设计。

请提交解决方案:课题需将爬虫构建相关PPT和代码作为研究的最终成果,并在PPT中简要展示以下几点内容:

(1)构建爬虫和舆情分析的主要流程与模块;

(2)项目过程中使用到的工具,遇到的困难和问题,以及解决的方式;

(3)简要评价爬虫的效率与性能;

(4)最终基于微博信息进行舆情分析的结果展示;


1.爬虫与舆情分析的主要流程

1.1爬虫切入点

本课题主要对新浪微博进行爬虫,但是遗憾的是新浪微博并没有提供以“关键字+时间+区域”方式获取官方API。但是庆幸的是,新浪提供了高级搜索功能。


 招商银行fintech选拔课题---《基于微博爬虫的舆情分析》上_第1张图片          


点击搜索微博后,我们看地址栏:

http://s.weibo.com/weibo/%25E6%258B%259B%25E5%2595%2586%25E9%2593%25B6%25E8%25A1%258C&typeall=1&suball=1×cope=custom:2017-05-02:2017-05-02&Refer=g

解析如下固定地址部分:      http://s.weibo.com/wb/

关键字(2URLEncode编码):  %25E6%258B%259B%25E5%2595%2586%25E9%2593%25B6%25E8%25A1%258C

搜索时间范围:           timescope=custom:2017-05-02:2017-05-02

可忽略项:             Refer=g

某次请求的页数(未出现):  page=1(某页请求页数)


既然是这么回事,我们接下来就可以使用网页爬虫的方式获取“关键字+时间”的微博了……

1.2爬虫思路

本课题所采用的爬虫语言是Python。在对新浪微博进行爬虫之前,首先需要模拟登陆,这里所采用的办法是:使用rsa加密模块进行模拟登陆。接下来要构造URL,爬取网页,然后解析网页中的微博信息,如图所示。

另外,高级搜索最多返回 50 页微博。时间范围( timescope )可设置为 1 天,如 2017-05-02:201-05-02

招商银行fintech选拔课题---《基于微博爬虫的舆情分析》上_第2张图片

爬虫代码如下:
爬虫新浪微博需要先模拟登陆,这里采用rsa加密模块进行模拟登陆。
#coding=utf8
   
import urllib
import urllib2
import cookielib
import base64
import re
import json
import hashlib
import rsa
import binascii

cj = cookielib.LWPCookieJar()
cookie_support = urllib2.HTTPCookieProcessor(cj)
opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)
urllib2.install_opener(opener)
postdata = {
     'entry': 'weibo',
     'gateway': '1',
     'from': '',
     'savestate': '7',
     'userticket': '1',
     'ssosimplelogin': '1',
     'vsnf': '1',
      'vsnval': '',
      'su': '',
      'service': 'miniblog',
      'servertime': '',
      'nonce': '',
      'pwencode': 'rsa2', #加密算法
      'sp': '',
      'encoding': 'UTF-8',
      'prelt': '401',
      'rsakv': '',
      'url': 'http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack',
      'returntype': 'META'
}
  
class WeiboLogin:
     def __init__(self, username, password):
          self.username = username
          self.password = password
      
     def __get_spwd(self):
          rsaPublickey = int(self.pubkey, 16)
          key = rsa.PublicKey(rsaPublickey, 65537) #创建公钥
          message = self.servertime + '\t' + self.nonce + '\n' + self.password #拼接明文js加密文件中得到
          passwd = rsa.encrypt(message, key) #加密
          passwd = binascii.b2a_hex(passwd) #将加密信息转换为16进制。
          return passwd
  
     def __get_suser(self):
         username_ = urllib.quote(self.username)
         username = base64.encodestring(username_)[:-1]
         return username
    
     def __prelogin(self):
          prelogin_url = 'http://login.sina.com.cn/sso/prelogin.php?entry=sso&callback=sinaSSOController.preloginCallBack&su=%s&rsakt=mod&client=ssologin.js(v1.4.4)' % self.username
          response = urllib2.urlopen(prelogin_url)
          p = re.compile(r'\((.*?)\)')
          strurl = p.search(response.read()).group(1)
          dic = dict(eval(strurl)) #json格式的response
          self.pubkey = str(dic.get('pubkey'))
          self.servertime = str(dic.get('servertime'))
          self.nonce = str(dic.get('nonce'))
          self.rsakv = str(dic.get('rsakv'))

     def login(self):
          url = 'http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)'
          try:
              self.__prelogin() #预登录
          except:
              print 'Prelogin Error'
              return
          global postdata
          postdata['servertime'] = self.servertime
          postdata['nonce'] = self.nonce
          postdata['su'] = self.__get_suser()
          postdata['sp'] = self.__get_spwd()
          postdata['rsakv'] = self.rsakv
          postdata = urllib.urlencode(postdata)
          headers = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:37.0) Gecko/20100101 Firefox/37.0'}
          req  = urllib2.Request(
              url = url,
              data = postdata,
              headers = headers
          )
          result = urllib2.urlopen(req)
          text = result.read()
          p = re.compile('location\.replace\(\'(.*?)\'\)')
          try:
              login_url = p.search(text).group(1)
              urllib2.urlopen(login_url)
              print "Login Succeed!"
          except:
              print 'Login Error!'

爬虫模块代码,注意这里把爬虫得到的代码写入了EXCEL中,保存。

# coding: utf-8


'''
以关键词收集新浪微博
'''
import wx
import sys
import urllib
import urllib2
import re
import json
import hashlib
import os
import time
from datetime import datetime
from datetime import timedelta
import random
from lxml import etree
import logging
import xlwt
import xlrd
from xlutils.copy import copy
from datetime import datetime


class CollectData():
    """数据收集类
       利用微博高级搜索功能,按关键字搜集一定时间范围内的微博。
    """
    def __init__(self, keyword, startTime, interval='50', flag=True, begin_url_per = "http://s.weibo.com/weibo/"):
        self.begin_url_per = begin_url_per  #设置固定地址部分,默认为"http://s.weibo.com/weibo/"
        self.setKeyword(keyword)    #设置关键字
        self.setStartTimescope(startTime)   #设置搜索的开始时间
        #self.setRegion(region)  #设置搜索区域
        self.setInterval(interval)  #设置邻近网页请求之间的基础时间间隔(注意:过于频繁会被认为是机器人)
        self.setFlag(flag)  #设置
        self.logger = logging.getLogger('main.CollectData') #初始化日志

    ##设置关键字
    ##关键字需解码
    def setKeyword(self, keyword):
        self.keyword = keyword.decode('GBK').encode("utf-8") #先将其GBK解码,然后再UTF-8编码,然后再输出:
        print 'twice encode:',self.getKeyWord()

    ##设置起始范围,间隔为1天
    ##格式为:yyyy-mm-dd
    def setStartTimescope(self, startTime):
        if not (startTime == '-'):
            self.timescope = startTime + ":" + startTime
        else:
            self.timescope = '-'

    ##设置搜索地区
    #def setRegion(self, region):
    #    self.region = region

    ##设置邻近网页请求之间的基础时间间隔
    def setInterval(self, interval):
        self.interval = int(interval)

    ##设置是否被认为机器人的标志。若为False,需要进入页面,手动输入验证码
    def setFlag(self, flag):
        self.flag = flag

    ##构建URL
    def getURL(self):
        return self.begin_url_per+self.getKeyWord()+"&typeall=1&suball=1×cope=custom:"+self.timescope+"&page="
     ##固定地址+关键字二次UTF-8编码+
     ##http://s.weibo.com/weibo/%25E8%2583%2596%25E7%25BA%25B8%25E5%2592%258C%25E7%2598%25A6%25E7%25BA%25B8%25E7
	 ##%259A%2584%25E5%258C%25BA%25E5%2588%25AB&typeall=1&suball=1×cope=custom:2017-05-01-0:2017-05-02-0&Refer=g
    ##关键字需要进行两次urlencode
    def getKeyWord(self):
        once = urllib.urlencode({"kw":self.keyword})[3:]  #首先把中文字符转换为十六进制,然后在每个字符前面加一个标识符%。
        return urllib.urlencode({"kw":once})[3:]

    ##爬取一次请求中的所有网页,最多返回50页
    def download(self, url, maxTryNum=4):
        hasMore = True  #某次请求可能少于50页,设置标记,判断是否还有下一页
        isCaught = False    #某次请求被认为是机器人,设置标记,判断是否被抓住。抓住后,需要复制log中的文件,进入页面,输入验证码
        name_filter = set([])    #过滤重复的微博ID  set 一个无序不重复元素集
        
        i = 1   #记录本次请求所返回的页数
        while hasMore and i < 51 and (not isCaught):    #最多返回50页,对每页进行解析,并写入结果文件
            source_url = url + str(i)   #构建某页的URL  在原来的基础上加上page后面的页码
            data = ''   #存储该页的网页数据
            goon = True #网络中断标记
            ##网络不好的情况,试着尝试请求三次
            for tryNum in range(maxTryNum): ##0-3
                try:
                    html = urllib2.urlopen(source_url, timeout=12)
                    data = html.read()
                    break
                except:
                    if tryNum < (maxTryNum-1):
                        time.sleep(10)
                    else:
                        print 'Internet Connect Error!'
                        self.logger.error('Internet Connect Error!')
                        self.logger.info('url: ' + source_url)
                        self.logger.info('fileNum: ' + str(fileNum))
                        self.logger.info('page: ' + str(i))
                        self.flag = False
                        goon = False
                        break
            if goon:
                lines = data.splitlines()   ##按照行分隔,返回一个包含各行作为元素的列表
                isCaught = True
                for line in lines:
                    ## 判断是否有微博内容,出现这一行,则说明没有被认为是机器人
                    if line.startswith('
                    
                    

你可能感兴趣的:(网络爬虫与数据分析)