如何科学地蹭热点:用python爬虫获取热门微博评论并进行情感分析

前言:本文主要涉及知识点包括新浪微博爬虫、python对数据库的简单读写、简单的列表数据去重、简单的自然语言处理(snowNLP模块、机器学习)。适合有一定编程基础,并对python有所了解的盆友阅读。

甩锅の声明

1.本数据节选自新浪热门微博评论,不代表本人任何观点

2.本人不接受任何非技术交流类批评指责(夸我可以)

3.本次分析结果因技术问题存在一定误差(是引入的包的问题,不是我的)

4.本次选取热门微博为一个月以前的(翻译一下:热点已经冷了,我只是个写教程的)

4.顶锅盖逃

继上次更完“国庆去哪儿”文之后,被好多编程相关的公众号翻了牌子_(:зゝ∠)_,让我过了一把v的瘾,也让我更加努力的想要装(消音)。

在我埋头学习mysql、scrapy、django准备下一波吹水的时候,有人说,你去爬下老薛的微博呀,还能蹭个热点,这让勤(mo)奋(mo)学(kou)习(jiao)的我停下了寄几敲代码的手。

然后我赶紧去关注了一下最近老薛的新闻…在感受了剧情的复杂和案情的扑朔迷离之后…我默默地学习了如何阅读合同…如何利用ELA分析图片…如何写作文…如何查别人的银行流水…知识点有点多…让我缓一会…

所以呢,这次的主题是分析老薛最新微博的评论,分析评论粉丝们的心情状态,且听我娓娓道来。

1.新浪微博API

在经历了几次爬虫被禁的悲痛(真的很痛)之后,我学会了在爬网站之前先查有没有API的“优良”习惯。新浪作为一个大厂,怎么会不推出新浪微博API呢,面向开发者新浪有自己的开放平台,这里是python调用微博API的方法,下面是通过登录App_key和App_secret方式访问微博API的代码,代码是基于py2的。py3对weibo模块使用存在一定问题。

from weibo import APIClient 
import webbrowser

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

APP_KEY = '你的App Key ' #获取的App Key 
APP_SECRET = '你的AppSecret' #获取的AppSecret 
CALLBACK_URL = 'https://api.weibo.com/oauth2/default.html' #回调链接 

client = APIClient(app_key=APP_KEY, app_secret=APP_SECRET, redirect_uri=CALLBACK_URL) 
url = client.get_authorize_url() 
webbrowser.open_new(url) #打开默认浏览器获取code参数 

print '输入url中code后面的内容后按回车键:'

code = raw_input()
r = client.request_access_token(code)
access_token = r.access_token
expires_in = r.expires_in
client.set_access_token(access_token, expires_in)

知道如何登录API了,辣么如何调用API爬单条微博的评论呢?一行代码搞定。

r = client.comments.show.get(id = 4154417035431509,count = 200,page = 1)

所有关于单条微博的评论信息都在r.comments中了,这里需要对照微博API文档,微博API有声明调用微博评论API需要获取用户授权,但是捏,只要知道单条微博的id,就可以调用这个API了,关于单条微博的id如何获取在后面会说(小声一点,千万别让微博知道,万一封了呢)。

按照client.接口名.get(请求参数)的方式获取API,获取API后的规格可在接口详情中查看,文档中有给出返回结果的示例。

文档中也给出了关键数据的json接口名称。

如果我们要获取微博评论的内容,只需要调用text接口即可。

for st in r.comments:
	text = st.text

2.微博爬虫

通过调用新浪微博API的方式,我们就可以简单获取单条微博的评论信息了,为啥说简单呢,因为人红信息贵啊!你以为大V的微博就这么免费的给你API调用了吗,非认证应用开发者单日只能请求几千次API,这对像老薛这样单条微博几十万评论的大V来说…太少了(TT)

所以捏,还是要写微博爬虫。

正所谓,知己知彼百战不殆,新浪作为大厂,怎么说也是身经百战,必定是经历了无数场爬虫与反爬之间的战争,必然有着健全的反爬策略。正所谓,强敌面前,绕道而行,有位大佬说得好,爬网站,先爬移动端:https://m.weibo.cn/

登录微博后,进入到老薛回应P图事件的微博中去,_(:зゝ∠)_老薛啊,怪我,怪我来得太晚了,点进去的时候已经有70w+的评论了(截止至发文当天已经100w+的评论了),可以看到安静的微博下粉丝们不安的心…

移动端微博的网址显得肥肠简单,不似PC端那么复杂而不明逻辑:https://m.weibo.cn/status/4154417035431509 多点几条微博就可以知道status后面的数字,就是单条微博的id了。

评论里包含了热门评论和最新评论俩种,但无论是哪种评论,继续往下翻网址都不会变化。江湖惯例(不懂江湖惯例的去看我之前的文),chrome浏览器右键“检查”,观察network变化。

从network的xhr文件中,可以得知热门评论的变化规律是:

'https://m.weibo.cn/single/rcList?format=cards&id=' + 单条微博id + '&type=comment&hot=1&page=' + 页码

最新评论的变化规律是:

'https://m.weibo.cn/api/comments/show?id=' + 单条微博id + '&page=' + 页码

打开https://m.weibo.cn/single/rcList?format=cards&id=4154417035431509&type=comment&hot=1&page=1 就可以看到热门评论的json文件。

接下来就是套路了,伪装浏览器header,读取json文件,遍历每一页…这都不是重点!而且这些我以前都讲过~直接上代码~这里开始是py3的代码了~

import re,time,requests,urllib.request

weibo_id = input('输入单条微博ID:')
# url='https://m.weibo.cn/single/rcList?format=cards&id=' + weibo_id + '&type=comment&hot=1&page={}' #爬热门评论
url='https://m.weibo.cn/api/comments/show?id=' + weibo_id + '&page={}' #爬时间排序评论
headers = {
    'User-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:55.0) Gecko/20100101 Firefox/55.0',
    'Host' : 'm.weibo.cn',
    'Accept' : 'application/json, text/plain, */*',
    'Accept-Language' : 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
    'Accept-Encoding' : 'gzip, deflate, br',
    'Referer' : 'https://m.weibo.cn/status/' + weibo_id,
    'Cookie' : '登录cookie信息',
    'DNT' : '1',
    'Connection' : 'keep-alive',
    }
i = 0
comment_num = 1
while True:
    # if i==1:     #爬热门评论
    #     r = requests.get(url = url.format(i),headers = headers)
    #     comment_page = r.json()[1]['card_group']
    # else:
    #     r = requests.get(url = url.format(i),headers = headers)
    #     comment_page = r.json()[0]['card_group']
    r = requests.get(url = url.format(i),headers = headers)  #爬时间排序评论
    comment_page = r.json()['data']
    if r.status_code ==200:
        try:
            print('正在读取第 %s 页评论:' % i)
            for j in range(0,len(comment_page)):
                print('第 %s 条评论' % comment_num)
                user = comment_page[j]
                comment_id = user['user']['id']
                print(comment_id)
                user_name = user['user']['screen_name']
                print(user_name)
                created_at = user['created_at']
                print(created_at)
                text = re.sub('<.*?>|回复<.*?>:|[U00010000-U0010ffff]|[uD800-uDBFF][uDC00-uDFFF]','',user['text'])
                print(text)
                likenum = user['like_counts']
                print(likenum)
                source = re.sub('[U00010000-U0010ffff]|[uD800-uDBFF][uDC00-uDFFF]','',user['source'])
                print(source + '
')
                comment_num+=1
            i+=1
            time.sleep(3)
        except:
            i+1
            pass
    else:
        break

这里有几点说明:
1.设置爬取间隔时间之后,微博爬虫被禁的概率降低了很多(特别是晚上)
2.新浪每次返回的json数据条数随机,所以翻页之后会出现数据重复的情况,所以用到了数据去重,这会在后面说。
3.在text和source中添加了去除emoji表情的代码(折腾了很久写不进数据库,差点就从删库到跑路了/(ㄒoㄒ)/),同时也去除了掺杂其中的回复他人的html代码。
4.我只写了读取数据,没有写如何保存,因为我们要用到数!据!库!辣!(这是重点!敲黑板)

3.python中数据库的读取与写入

虽然微博爬虫大大提高了数据获取量,但也因为是爬虫而容易被新浪封禁。这里结束循环的判断是网络状态不是200,但当微博发现是爬虫时,微博会返回一个网页,网页中什么实质内容都木有,这时候程序就会报错,而之前爬到的数据,就啥也没有了。

啥 都 没 有 了

但是如果爬一会,保存一次数据,这数据量要一大起来…冷冷的文件在脸上胡乱地拍…我的心就像被…这时候我们就需要用到数据库了。

数据库,顾名思义,就是存放数据的仓库,数据库作为一个发展了60多年的管理系统,有着庞大的应用领域和复杂的功能……好了我编不下去了。

在本文中,数据库的主要作用是AI式的excel表格(●—●)。在爬虫进行的过程中,爬到一个数就存进去,爬到一个数就存进去,即使爬虫程序运行中断,中断前爬到的数据都会存放在数据库中。

配图.gif

大多数数据库都能与python对接使用的,米酱知道的有mysql、sqlite、mongodb、redis。这里用的是mysql,mac上mysql的安装,管理数据库的软件Navicat使用帮助,其他系统自己找吧,安装使用过程中有啥问题,请不要来找我(逃

根据上面的代码,在navicat中创建数据库、表和域以及域的格式。在Python程序中添加代码。

conn =pymysql.connect(host='服务器IP(默认是127.0.0.1)',user='服务器名(默认是root)',password='服务器密码',charset="utf8",use_unicode = False)    #连接服务器
cur = conn.cursor()
sql = "insert into xue.xueresponse(comment_id,user_name,created_at,text,likenum,source) values(%s,%s,%s,%s,%s,%s)" #格式是:数据名.表名(域名)
param = (comment_id,user_name,created_at,text,likenum,source)
try:
    A = cur.execute(sql,param)
    conn.commit()
except Exception as e:
    print(e)
    conn.rollback()

运行python程序,大概爬了2w条实时评论,新浪微博的反爬还是很强大的,有俩个解决方法:更换IP和切换账号,IP代理的使用方法我以前有写过,账号可以在X宝购买,但是!_(:зゝ∠)_由于本文的作者是一个肥肠贫穷肥肠抠门并且身患重病(懒癌)的人……2w条数据也是有研究价值的对不对(((;꒪ꈊ꒪;))),不如我们就这样继续我们研究吧(((;꒪ꈊ꒪;)))……

在进行下一步研究之前,我们还要将数据库中的内容读取出来,python中数据库的读取代码也很简单。

conn =pymysql.connect(host='服务器IP',user='用户名',password='密码',charset="utf8")    #连接服务器
with conn:
    cur = conn.cursor()
    cur.execute("SELECT * FROM xue.xueresponse WHERE id < '%d'" % 20000)
    rows = cur.fetchall()

这样之前爬取的信息就被读取出来了,但是前面也说了,微博爬虫翻页时返回数据条数随机,所以会出现重复的状况,所以读取之后,需要用if…not in语句进行一个数据去重。

for row in rows:
    row = list(row)
    del row[0]
    if row not in commentlist:
        commentlist.append([row[0],row[1],row[2],row[3],row[4],row[5]])

完整代码在文末。

4.自然语言处理NLP

NLP是人工智能的一个领域,可以通过算法的设计让机器理解人类语言,自然语言也属于人工智能中较为困难的一环,像中文这么博大精深、变幻莫测的语言更是NLP中的一大难点,python中有很多NLP相关的模块,有兴趣的盆友可以通过用python实现简单的文本情感分析初探NLP。

我参(ban)考(yun)了一些现成的情感分析算法,对爬取的评论进行分析,错误率肥肠高_(:зゝ∠)_,这可肿么办?难道要重新设计算法?米酱仿佛遇到了人生中第一个因为语文没学好而引发的重大问题……

当然像米酱这样灵(lan)活(duo)的姑娘,自然是很快发现了python中较为出名的一个中文NLP库:snowNLP。snowNLP调用的方法比较简单,源码中详细解释了调用方法,和生成结果。

def snowanalysis(textlist):
    sentimentslist = []
    for li in textlist:
        s = SnowNLP(li)
        print(li)
        print(s.sentiments)
        sentimentslist.append(s.sentiments)

这段代码中获取了读取数据库后由评论主体text生成的列表文件,并依次对每一个评论进行情感值分析。snowNLP能够根据给出的句子生成一个0-1之间的值,当值大于0.5时代表句子的情感极性偏向积极,当分值小于0.5时,情感极性偏向消极,当然越偏向俩头,情绪越明显咯,让我们来看看测试评论的结果。

第一条:恶心 呸 给出了0.01的分值,最后一条:力挺薛之谦 给出了0.99的分值。看其他几句基本符合语境的态度,当需要评测单个商品的评价态度时就可以使用snowNLP。但是由于老薛的评论中涉及到三个人,他自己,李雨桐,高磊鑫,算法无法判断评论是关于谁的情感值,又因为微博评论可以粉丝之间互相回复,这让判断评论的主体是谁更加扑朔迷离(机器表示我学习不能啊…)。

这好像代表了,本次分析的结果将…并没有什么卵用。发生这种事情,米酱也不想的…做人呢…最重要的是要开心…米酱不会写算法啊,米酱语文也不好啊,米酱真的做不到啊(๑°⌓°๑)…

5.分析结果

本次分析的目的愉快的从分析粉丝们对待薛之谦事件的情绪变成了单纯分析粉丝们的情绪(●—●)。

plt.hist(sentimentslist,bins=np.arange(0,1,0.02))
plt.show()

对上节经过处理得到的情感值列表进行统计,并生成分布图。下图数据采集时间9月27日19时,采集评论2w条。

薛之谦回应P图事件微博评论情感值分布

我还顺便(真的是顺便,正经脸)爬了李雨桐将网友转账捐款的微博的2w条评论。

李雨桐将网友转账捐款微博评论情感值分布

我又顺顺便(真的顺便,看我真诚的眼神)爬了陈赫出轨时发表我错了博文的微博评论。

陈赫发表我错了博文评论情感值分布

根据这三张图,可以看到情感值在接近0、1两端以及0.5左右位置频率较高,说明粉丝们对于此类事件的情绪无论是积极还是消极都是比较明显的。但也可以从图片中看到一些微妙的差别,老薛的微博中情感值接近1的数量高于接近0的数量,但都没有超过700,李雨桐微博中情感值接近1的数量远低于接近0的数量,甚至接近0的数目到达了1000以上,但二者都超过了700,而陈赫的微博分析结果,俩者则都接近800。由于算法存在一定误差,不代表真实评论结果,我就不再多分析了(你们懂的)。

6.彩蛋

由于本次分析结果十分的……苍白(我真的…尽力了…_(:зゝ∠)_

我又对评论中出现的微博表情进行了统计。

#薛微博评论表情统计
Counter({'[加油]': 128, '[哆啦A梦开心]': 28, '[哆啦A梦亲亲]': 17, '[哆啦A梦美味]': 12, '[em]': 4, '[/em]': 4, '[愛你]': 4, '[攤手]': 3, '[怒罵]': 3, '[好棒]': 3, '[转发]': 2, '[可愛]': 1, '[太開心]': 1, '[/cp]': 1, '[呵呵]': 1, '[xkl转圈]': 1, '[温暖帽子]': 1, '[ok]': 1})
#李微博评论表情统计
Counter({'[攤手]': 13, '[加油]': 10, '[ok]': 8, '[皱眉]': 6, '[怒罵]': 4, '[太開心]': 3, '[左哼哼]': 3, '[饞嘴]': 2, '[擠眼]': 2, '[呵呵]': 2, '[嘿哈]': 2, '[机智]': 2, '[/cp]': 1, '[抱抱_旧]': 1, '[笑而不語]': 1, '[費解]': 1, '[cp]': 1})
#陈微博评论表情统计
Counter({'[呵呵]': 238, '[挖鼻屎]': 77, '[微風]': 45, '[好棒]': 26, '[打哈气]': 21, '[ok]': 12, '[左哼哼]': 12, '[羊年大吉]': 12, '[懒得理你]': 11, '[崩溃]': 10, '[花心]': 9, '[困死了]': 8, '[淚]': 8, '[玫瑰]': 8, '[睡觉]': 6, '[雷锋]': 5, '[炸鸡和啤酒]': 5, '[带着微博去旅行]': 4, '[有鸭梨]': 3, '[发红包啦]': 3, '[马到成功]': 3, '[丘比特]': 3, '[最右]': 3, '[花]': 3, '[打臉]': 3, '[别烦我]': 2, '[推荐]': 2, '[抠鼻屎]': 2, '[傷心]': 2, '[xkl转圈]': 2, '[霹雳]': 2, '[em]': 2, '[/em]': 2, '[悲催]': 2, '[不要]': 2, '[ali转圈哭]': 2, '[xkl糖豆]': 1, '[江南style]': 1, '[芒果萌萌哒]': 1, '[给劲]': 1, '[歪果仁夏克立]': 1, '[抢到啦]': 1, '[萌娃大竣]': 1, '[电影]': 1, '[愤怒]': 1, '[夏天公主]': 1, '[飞个吻]': 1, '[父亲节]': 1, '[强]': 1, '[得瑟]': 1, '[被电]': 1, '[拜 拜]': 1, '[蠟燭]': 1, '[奧特曼]': 1, '[lt吓]': 1, '[甩甩手]': 1, '[转发]': 1, '[xkl顶]': 1, '[贊]': 1, '[愉快]': 1, '[纠结]': 1, '[/cp]': 1, '[bm赞]': 1, '[巨汗]': 1, '[最差]': 1, '[害怕]': 1, '[豬頭]': 1, '[bm哭泣]': 1, '[lt泪目]': 1, '[瞧瞧]': 1, '[打哈欠]': 1, '[xkl亲亲]': 1, '[bm会心笑]': 1, '[bm调皮]': 1})

李相比于俩位当时正当红的明星,粉丝们使用表情的数目也是天壤之别啊。网友们对陈赫评论最多的表情是[呵呵],没有一个[加油](也有可能当时没有[加油]这个表情?),对老薛用了128次[加油],李雨桐10次,而评论李雨桐微博最多的表情也只有13个。果然是人红热点多,连表情包也多啊!

我还对参与评论的粉丝进行了统计,并提取了2w条微博评论里参与评论最多的前20位粉丝。

#薛微博评论粉丝统计
[('方鹿亓', 150), ('用户5238901365', 60), ('野百合xy', 57), ('忆盲i', 46), ('哎哟玛', 44), ('学霸吖', 43), ('炒股屌丝', 42), ('赌神美好如初52', 38), ('梅兰竹菊-世外桃源', 37), ('罂粟七年前', 36), ('阳光音乐i', 33), ('用户5909206841', 33), ('林xiu霞', 32), ('医闹薛之谦', 32), ('wyx518052', 31), ('午后阳光喵喵', 29), ('蓝天太阳我喜他', 29), ('不死就作的sr', 29), ('李遇可爱啵', 29), ('家驹一生所爱', 28)]
#李微博评论粉丝统计
[('方鹿亓', 139), ('0o夜雨狂澜o0', 120), ('蓝魅丶影', 98), ('mingyuanmumei', 76), ('晓清Z', 75), ('谦谦家的暴发户', 65), ('一个玥宝贝儿', 64), ('qinglongzhilian', 62), ('出门左转滚', 60), ('用户5909206841', 58), ('水里的鱼188啊', 56), ('story他说', 53), ('C平民', 52), ('断断续续的思考', 50), ('铿客HXRK', 48), ('我的网名只有8个字', 48), ('爱吃鱼的猫妮', 47), ('唯ai薛之谦', 46), ('用户6213147659', 45), ('洛杉矶的咖喱鸡', 44)]

emmmmmm,这个我就不分析了,大家寄几理解吧。那个叫方鹿亓的,你出来,你说说,你咋那么闲呢?(逃

_(┐「ε:)_终于编…哦不…写完了,大家慢慢消化,源码已上传至gayhub

本文版权归大吉大利小米酱所有,未经授权不得转载

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注