抓取B站评论并可视化


写在前面

最近一位很火的央视美女主持,没错,说的就是王冰冰,入驻B站啦,她在2020年的最后一天发布了一条vlog,疯狂涨粉200w,成功跻身2020百大up,那么B站的网友们在评论区说了什么呢?让我们一起来看看吧!

思路分析

B站评论接口如下:


https://api.bilibili.com/x/v2/reply


参数有:

#固定值,可省略
'jsonp':'jsonp'
#固定值,不可缺省
'type':'1'
#评论页数
"pn":1
#排序方式,2代表从旧到新,1代表从新到旧,默认为1
'sort':'2'
#视频的av号,可以通过开发者工具获取
'oid':"800760067"
#毫秒级时间戳,可省略
'_':"1609912833995"

代码部分

用到的包

#网页请求
import requests
#获取时间戳
import time
#连接到MongoDB
import pymongo
#操作excel
import openpyxl
#进度条
from tqdm import tqdm
#处理数据
import pandas as pd
#结巴分词
import jieba
#词云
from wordcloud import WordCloud
#统计词频
from tkinter import _flatten
#默认停用词
from wordcloud import STOPWORDS
#加载词云背景图
import matplotlib.pyplot as plt

Bilibili类

#修改oid可以抓取不同视频的评论
oid = '800760067'
class Bilibili(object):
    #初始化,从外部获取oid和想要抓取的总页数(从第一页开始算起)
    def __init__(self,oid,pn):
        #评论接口
        self.url = 'https://api.bilibili.com/x/v2/reply'
        #参数,评论排序方式为从旧到新
        self.params = {
                'jsonp':'jsonp',
                'type':'1',
                'sort':'2',
                'oid':oid,
                '_':self.get_curl(),
                }
        #请求头
        self.headers = {
             'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
            }
        #要抓取的总页数
        self.pn = pn
        #连接到MongoDB,改成自己的用户名和密码
        self.client = pymongo.MongoClient("mongodb://username:pwd@localhost:27017")
        #指定数据库,不存在则自动创建
        self.db = self.client.bilibili
        #指定表,不存在则自定创建
        self.collection = self.db.comment
        #初始化excel工作表
        self.wb = openpyxl.Workbook()
        #创建一个默认sheet
        self.ws = self.wb.active
        #创建一个文本文件
        self.file = open('bilibili.txt','wt',encoding = 'utf-8')

    #获取毫秒级时间戳
    def get_curl(self):
        return str(round(time.time() * 1000))

    #python内置装饰器,将类中函数装饰为属性
    @property 
    def get_comment(self):
        #为excel表格添加索引
        self.ws.append(['用户','性别','评论','等级','点赞'])
        #循环抓取并启用进度条
        for i in tqdm(range(1,self.pn + 1),desc = '正在下载...',ncols = 80):
            #更新参数
            self.params['pn'] = i
            #有时候会出现某一页无响应,捕捉一下异常
            try:
                #发起get请求,将超时时间设置为5s,超时则会引发异常进入except
                res = requests.get(url = self.url,headers = self.headers,params = self.params,timeout = 5)
                #将response转换为json格式并获取当前页所有评论
                self.comments = res.json()['data']['replies']
                #逐条分析
                for comment in self.comments:
                    #发表评论的用户名
                    username = comment['member']['uname']
                    #用户性别
                    sex = comment['member']['sex']
                    #评论内容
                    content = comment['content']['message']
                    #用户等级
                    level = comment['member']['level_info']['current_level']
                    #点赞数量
                    like = comment['like']
                    #封装为字典
                    reply = {"用户":username,"性别":sex,"评论":content,"等级":level,"点赞":like}
                    #向MongoDB中插入一条数据
                    self.collection.insert_one(reply)
                    #向excel表格中插入一条数据
                    self.ws.append([username,sex,content,level,like])
                    #将评论内容写入txt文件
                    self.file.write(content + '\n')
                #暂停2s
                time.sleep(2)
            #遇到异常的话无视,继续下一页
            except:
                pass
    #将类中函数装饰为属性
    @property
    def save(self):
        #调用前述方法获取评论
        self.get_comment
        #保存excel表
        self.wb.save('bilibili.xlsx')
        #关闭文本文件
        self.file.close()

    #写一个内容类用于制作词云
    class Word_Cloud(object):
        def __init__(self):
            #从excel中读取数据
            self.data = pd.read_excel('bilibili.xlsx')
            #自定义停用词
            self.words = ['量', '…', '还', '🍑','\u3000',' ','🧊','▓','\xa0',"啊","这",'・',"就","在","站","你",'\n','_','的','[',']','?','!','那','都','是','吧','个','我','和','有','呢','号','\\','(',')','(',')','doge',';',';','。','@',',',':','!','https','/','“','”','了',]
            #词云背景图
            self.img="1.jpeg"
            #将默认停用词与自定义停用词合并(python的列表居然可以直接加!)
            self.stopWords = list(STOPWORDS) + self.words

        #装饰为属性
        @property
        def word_cloud(self):
            #自定义词库,这里是一个示例,不加的话会把“打call”拆开
            jieba.load_userdict(['打call'])
            #获取评论并调用jieba分词
            dataCut = self.data['评论'].apply(jieba.lcut)
            #去除停用词,并去除单字符词
            dataAfter = dataCut.apply(lambda x: [i for i in x if i not in self.stopWords and len(i) > 1])
            #统计词频
            wordFre = pd.Series(_flatten(list(dataAfter))).value_counts()
            #加载背景图
            mask = plt.imread(self.img)
            #创建wordcloud对象,参数分别为:放大比例(与原始画布)、字体、背景图、背景颜色
            wc = WordCloud(scale=10,font_path='C:/Windows/Fonts/simsun.ttc',mask=mask,background_color="white",)
            #将分好的词填充到词云
            wc.fit_words(wordFre)
            #保存词云图
            wc.to_file('bilibili.png')

从txt文件生成词云:

from collections import Counter
class Word_Cloud(object):
    def __init__(self):
        self.cn_words = ['量', '…', '还', '🍑','\u3000',' ','🧊','▓','\xa0',"啊","这",'・',"就","在","站","你",'\n','_','的','[',']','?','!','那','都','是','吧','个','我','和','有','呢','号','\\','(',')','(',')','doge',';',';','。','@',',',':','!','https','/','“','”','了',]
        self.img="1.jpeg"
        self.stopWords = list(STOPWORDS) + self.cn_words
        #读取文本文件
        self.f = open('bingbing.txt','r',encoding = 'utf-8').read()

    @property
    def word_cloud(self):
        #添加自定义词库
        jieba.load_userdict(['打call'])
        #分词
        words_cut = jieba.lcut(self.f)
        #去除停用词和单字符
        words = [i for i in words_cut if i not in self.stopWords and len(i) > 1]
        mask = plt.imread(self.img)
        #统计词频并按从大到小排序,这里是根据元组的第二个属性,reverse为True则按倒序排序
        count_list = sorted(Counter(words).items(), key = lambda x:x[1],reverse = True)
        #输出高频词汇
        print(count_list[0:20])
        #生成词云
        wc = WordCloud(scale=5,font_path='C:/Windows/Fonts/simsun.ttc',mask=mask,background_color="white",)
        wc.fit_words(dict(count_list))
        wc.to_file('bilibili.png')

调用方式

#修改pn为你要抓的总页数
bilibili = Bilibili(oid = oid,pn = 0)
bilibili.save
bilibili.Word_Cloud().word_cloud

词云背景图


美女主持王冰冰


写在后面

词云部分参考了CSDN的文章,另外,本文的爬虫部分更适合用scrapy来写,调度起来十分方便,有兴趣的读者可以研究一下。