短视频评论的抓取及分析
一.设计背景
目前,短视频已经成为大多数人娱乐消遣的主要方式。用户在观看视频内容的同时,也同样关注视频评论,并且很多时候评论带给人们的乐趣远远超过视频本身。但是各短视频平台都没有提供用户评论数据的可视化分析,基于该现状,本项目给出了解决方案。
在本项目中,我们对视频评论的文本进行分析,提取出现频率较高的一部分词汇形成词云,这样可以过滤掉大量文本,使用户一眼扫过就可以获取主要信息,并感知最突出且具有代表性的评论词汇;同时我们对每一条评论进行情感分析,统计各种情感倾向评论的数量,以可视化的方式展现给用户,以便用户掌握评论的舆情走向。除此之外,我们还对所有评论的IP进行分析,最终以地图的方式向用户展示全国各地的评论人数,使用户对该视频的地域性有所了解。
二.设计思想
1.评论数据抓取
首先分析视频页面源码以及服务器返回的各种数据,接下来定位评论数据,最后使用Fiddler抓包工具抓取服务器返回的JSON格式的评论数据并保存。
2.评论文本分析
(1)文本内容提取
分析JSON数据的层次,找到评论文本并提取。
(2)文本分析及可视化
(a)绘制词云
首先使用jieba库对全部评论分词并提取出现频率较高的词汇,然后利用wordcloud进行词云绘制。
(b)情感分析
首先调用百度智能云自然语言处理功能中的情感倾向分析,接下来对获取到的每一条评论进行情感分析,最后统计各种情感倾向的占比,并使用matplotlib绘制饼图,可视化展示分析结果。
3.评论IP分析
(1)IP信息提取
分析JSON数据层次,找到IP信息并提取。
(2)IP分析及可视化
使用collections库中的Counter方法对提取到的IP进行数量统计,得到每个省份评论人数的数据,然后使用pyecharts模块绘制体现各省份评论人数的中国地图。
三.数据获取流程
1.定位评论数据
通过分析页面源码发现,只有一小部分评论数据包含在页面源码中,显然直接爬取短视频页面的源码的做法并不可行。进一步分析发现,抖音评论数据为异步数据,即评论数据包随着用户下翻视频页面逐一从服务器发送给用户,每个数据包都为包含20条评论的JSON文件,并且具有唯一的URL。
2.数据抓取
首先尝试使用requests带着cookie直接请求该数据包的URL,但是发现返回的数据为空,经过多次尝试,最终认为直接爬取该数据包的难度较大。所以另辟蹊径,使用Fiddler抓包工具抓取在下翻评论过程中服务器返回的所有数据包,然后利用评论数据包URL的特征进行筛选,得到返回的所有评论数据,最后将这些数据保存以备接下来从中提取所需数据。
3.提取有用数据
将JSON文件内容复制粘贴到JSON解析网站,以便通过JSON视图更清晰的把握JSON数据的层次。
不难发现,评论文本信息和IP信息都在comments下以序号为标签的字典中,ip_label对应IP信息,text对应文本信息。由此可以编写Python代码提取每个JSON数据中的文本和IP信息。
四.代码结构
代码主要有两部分,comments_analysis:评论文本分析及可视化,形成词云和反应情感倾向占比的饼图;IP_analysis:对IP信息进行统计并绘制反应各省份评论人数的中国地图。代码结构图如下:
具体代码如下:
getComments.py
import json
import os
import re
import jieba.analyse
import wordcloud
from imageio import imread
from aip import AipNlp
import matplotlib.pyplot as pltmk = imread('词云背景图.png')def get_filepath(data_base_dir):"""拿到json文件的路径及文件名,保存到'file_name.txt中'"""file_name = open('file_name.txt', 'w', encoding='utf-8')filelist = []for filePath in os.listdir(data_base_dir):filePath = data_base_dir + '\\' + filePathfilelist.append(filePath)for i in filelist:file_name.write(i + '\n')file_name.close()def get_1comments(js_path):"""拿到一个json文件中的评论内容,保存到列表comment_1中并返回"""js = open(js_path, 'r', encoding='utf-8').read()data = json.loads(js)comment_1 = []for comment in data['comments']:comment_1.append(comment['text'])return comment_1def get_allcomments(data_base_dir):"""拿到所有json文件中的评论内容,保存到列表comments中并返回"""comments = []get_filepath(data_base_dir)file_name = open('file_name.txt', 'r', encoding='utf-8')for line in file_name:comments.extend(get_1comments(line.strip()))print('评论提取完毕!')return commentsdef form_wordcloud(comments):"""解析评论内容并形成词云"""word = ''.join(comments)# 使用jieba分词并提取出现频率前200的名词、动词、形容词等有代表性的词汇topWord_lis = jieba.analyse.extract_tags(word, topK=200, allowPOS='n''v''a''vn''an')topWord = ' '.join(topWord_lis)# 词云参数配置w = wordcloud.WordCloud(background_color='white',# font_path='C:\Windows\Fonts\HGY2_CNKI.TTF', # 字体width=2000,height=1500,max_words=1000,max_font_size=80,min_font_size=10,mode='RGBA',mask=mk, # 背景图)w.generate(topWord)w.to_file('评论词云.png')print('词云形成!')def get_emotion(comment):"""对一条评论进行情感分析,返回该评论的情感属性(emotion_label可能为0,1,2,分别对应消极的、中肯的、积极的)"""# 百度API认证APP_ID = '********'API_KEY = '********'SECRET_KEY = '********'client = AipNlp(APP_ID, API_KEY, SECRET_KEY)comment = ''.join(re.findall('[\u4e00-\u9fa5]', comment)) # 去除评论文本中的特殊字符# 调用情感倾向分析dic = client.sentimentClassify(comment)# 可能存在没有文本的空评论,异常处理保证程序执行的连续性try:emotion_label = dic['items'][0]['sentiment']except KeyError:return 1 # 若无内容,则为中性评论return emotion_labeldef emotion_analysis(comments):"""对所有评论进行情感分析并绘制饼图"""pos_comment = 0 # 积极rel_comment = 0 # 中肯neg_comment = 0 # 消极for comment in comments:if get_emotion(comment) == 0:neg_comment += 1elif get_emotion(comment) == 1:rel_comment += 1else:pos_comment += 1plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文支持plt.rcParams['axes.unicode_minus'] = False # 正常显示负号plt.style.use('fivethirtyeight') # 图表样式attitude = ['消极的', '积极的', '中肯的'] # 标签popularity = [neg_comment, pos_comment, rel_comment] # 数据# 绘制饼图plt.pie(popularity, labels=attitude, autopct='%1.1f%%',counterclock=False, startangle=90, explode=[0, 0.1, 0])plt.title('评论情感分析')plt.tight_layout()plt.savefig('情感分析结果.png')plt.show()print('情感分析完毕!')def main():data_base_dir = r'C:\Users\T-Rex\Desktop\PythonProject\douyin\data' # 数据文件的路径comments = get_allcomments(data_base_dir)form_wordcloud(comments)emotion_analysis(comments)if __name__ == "__main__":main()print('OK!')
get_ip.py
import json
import os
from collections import Counter
from pyecharts.charts import Map
from pyecharts.options import *def get_filepath(data_base_dir):"""拿到json文件的路径及文件名,保存到'file_name中'"""file_name = open('file_name', 'w', encoding='utf-8')filelist = []for filePath in os.listdir(data_base_dir):filePath = data_base_dir + '\\' + filePathfilelist.append(filePath)for i in filelist:file_name.write(i + '\n')file_name.close()def get_1data(js_path):"""拿到一个json文件中每条评论的IP,保存到列表ip中并返回"""js = open(js_path, 'r', encoding='utf-8').read()js_dic = json.loads(js)ip = []for comment in js_dic['comments']:ip.append(comment['ip_label'])return ipdef get_data(data_base_dir):"""提取所有json文件中评论的IP,保存到列表ip中并返回"""ip = []get_filepath(data_base_dir)file_name = open('file_name', 'r', encoding='utf-8')for line in file_name:ip.extend(get_1data(line.strip()))print('ip提取完毕!')return ipdef count(ip):"""统计所有IP出现的次数"""ip_count = Counter(ip)ipCount_list = []ip_place = list(ip_count.keys())ip_people = list(ip_count.values())# 将IP数据封装成('省份',人数)的元组形式,保存到列表ipCount_list中for i in range(len(ip_place)):ipCount_list.append((ip_place[i], ip_people[i]))print('统计完毕!')return ipCount_listdef form_map(ipCount_list):"""绘制IP分布地图"""MAP = Map()# 导入数据MAP.add("各省份评论人数", ipCount_list, "china")# 参数配置MAP.set_global_opts(title_opts=TitleOpts(title="IP分布地图"),visualmap_opts=VisualMapOpts(is_show=True, # 是否显示is_piecewise=True, # 是否分段pieces=[{"min": 1, "max": 20, "lable": "1~19人", "color": "#CCFFFF"},{"min": 20, "max": 40, "lable": "20~39人", "color": "#FFFF99"},{"min": 40, "max": 60, "lable": "40~59人", "color": "#FF9966"},{"min": 60, "max": 80, "lable": "60~79人", "color": "#FF6666"},{"min": 80, "max": 100, "lable": "80~99人", "color": "#CC3333"},{"min": 100, "lable": "100+", "color": "#990033"},]))# 绘图MAP.render("IP分布地图.html")print('绘图完毕!')def main():data_base_dir = r'C:\Users\T-Rex\Desktop\PythonProject\douyin\data' # 数据文件的路径ip = get_data(data_base_dir)ipCount_list = count(ip)form_map(ipCount_list)if __name__ == "__main__":main()print('OK!')
五.可视化展示及分析
1.评论词云
该词云展示出了全部评论中出现频率最高的词汇,如“医学”、“小白鼠”、“可爱”、“可怜”、“解剖”、“学医”等。通过词云用户可以对全部评论的主要信息一目了然,同时也可以看到评论中出现的“热词”。
2.情感倾向分布饼图
可见,持积极态度和消极态度的评论者数量均衡,只有一小部分评论者持中肯的态度。由此可知,此视频引发了较大的争议,评论者在评论区各执己见,互相抨击。
3.IP分布地图
从这张图可以看出,大部分评论者的IP为甘肃省。因为抖音会将用户创作的视频推给附近的人,所以可以推测,视频作者大概率身处甘肃。同时也可以看出,中东部地区以及新疆、广东评论者居多。
六.改进与完善
1.数据获取过程自动化程度降低,未来应进一步学习爬虫技术,找到可以直接通过URL一次性爬取全部评论数据的可行方法,同时提高代码的复用率。
2.词云中包含大量无用信息,如“不了”、“不用”、“觉得”、“感觉”、“看到”、“不想”、“时候”等。接下来应该编写一个包含常用中性词的字典,加入词云参数中用来过滤掉这些中性词。
3.情感倾向分析可能存在一定偏差。未来将进一步学习自然语言处理,利用针对短视频的评论情感倾向集来训练snownlp的情感分析模型,然后用该模型来进行情感分析以提高准确度。
4.继续学习pyecharts模块以及前端知识,使绘制的地图更美观。同时,地图图例中的数量分段应跟随评论总人数而变化,以期达到自适应的结果。
5.解决上述问题后,将设计一个可以嵌入浏览器的脚本,使用户在刷短视频的同时可以看到上述可视化成果。