python爬虫实例二——网易云音乐歌曲和评论的可视化数据分析

Python kingmo888 8957℃ 0评论

通过上一节写好的爬虫,我们爬完了所有的歌单、歌曲信息,总数量跟此前估计的差不多。接下来,我们做一些基本的分析。

分析目标:

1、统计评论过万的歌曲有哪些并导出到txt文本文件。

2、查看评论数top20的歌曲

3、将评论按照歌手划分,统计每个歌手的总评论数。

4、查看歌手所有歌曲总评论数top20

5、将歌手以评论数作为热度生成词云

6、统计歌手的产量(歌曲数量)。

7、查看歌手歌曲数量排行top20。

8、将歌手以产量(歌曲数量)作为热度生成词云

9、对所有评论分词后生成词云。

 

读取载入所有数据

在开始分析之前,我们先导入一些必要的库,

import os, json, copy
from lib import *
from song_comments import SongComments
import pandas as pd
import matplotlib.pyplot as plt
import wordcloud # 词云展示库
import collections
import numpy as np
from PIL import Image # 图像处理库

matplotlib支持中文

因为本篇文章涉及到绘图,图中的label等标签涉及到中文,而matplotlib对中文支持不好,额外设计函数使其支持中文,用法就是将本函数定义在脚本的上半部分,然后执行函数即可。

def set_ch():
    # 使python绘图支持中文
	from pylab import mpl
	mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
	mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
set_ch()

读取所有歌曲信息、评论信息

我将每首歌的信息都以单个json文件的形式存放到了tmp文件夹下,设计一个函数来解析读取到的json数据:

def deal_song_data(sdict):
    comm = pd.DataFrame(sdict['comments'])
    comm['description'] = sdict['description']
    comm['singer'] = sdict['singer']
    comm['song_name'] = sdict['song_name']
    comm['total'] = sdict['total']
    return comm

接下来,我们需要读取所有的歌曲json信息了,我们可以根据歌曲的id列表来构造文件的访问路径,也可以构造一个递归扫描函数来直接扫描整个文件目录,这里呢我们是有第一种方法。

在构造出文件路径后,还要对路径进行判断是否存在,当然,后续读取json文件的时候更合适的逻辑是要做一定的容错处理,这样才能保证爬虫的健壮性。因为数据量很小而且没有错误,在这里容错处理就暂时不做了。

读取歌曲id列表,

with open('all_sond_id', 'r') as f:
    all_song_id = f.read()
    all_song_id = all_song_id.split('\n')

开始逐个加载歌曲的信息json并添加到列表中,然后将列表拼接转换为pandas的dataframe:

all_json = []
song_infos = {}
for i,song_id in enumerate(all_song_id):
    if i % 100 == 0:
        print(i)
    jpath = 'tmp\{}.json'.format(song_id)
    if os.path.exists(jpath):
        with open(jpath, 'r') as f:
            tmp = json.load(f)
            all_json.append(tmp)
        
        tmp2 = copy.copy(tmp)
        tmp2.pop('comments')
        song_infos[song_id] = tmp2
            
all_data = list(map(deal_song_data, all_json))
all_data2 = pd.concat(all_data)

song_infos = pd.DataFrame(song_infos).T

最后将这个表格按照评论数量倒序排列,ascending参数控制排序方向,当False时为降序,True是为升序。

song_infos = song_infos.sort_values('total', ascending=False)

将评论数量大于10000的歌名导出到文本

# 统计评论过万的歌曲数量
sub_song_infos = song_infos[song_infos['total']>=10000]
print('歌曲评论过万数量有{}首'.format(len(sub_song_infos)))
# 把评论过万歌曲输出到txt文件中
with open('评论过万歌曲列表.txt', 'w', encoding='utf-8') as f:
    for i in range(len(sub_song_infos)):
        line = sub_song_infos.iloc[i]
        mystr = '{}-->      {} {}\n'.format(str(line['total']).ljust(10, ' '), line['song_name'],line['singer'])
        f.writelines(mystr)

我使用了字符串的format的格式化方法,将评论数量、歌名、歌手格式化为一行。

输出的部分结果如下:

接下来,就是绘图柱状图了,dataframe的head方法是获取df中前N行,tail方法则是获取df中后N行,df的set_index方法则是将制定的列设置为索引。

在这里选择前20行作为绘图目标并以歌名作为索引,方便后面绘图时设置标签。

fig_data = song_infos.head(20)
fig_data = fig_data.set_index('song_name')

figsize表示图形对象的尺寸,在此绘制一个14*10尺寸的图,值得注意的是:明明是柱状图,为啥高度设置比较高呢?

因为很多名字长度比较长,横排显示的话两个刻度间的标签文字会交叉覆盖,因此需要使用set_rotation方法将标签翻转90度,这样标签就会垂直显示了,高度若设置的比较小,标签内容将越界到图片外面会造成显示不全的问题。

fig = plt.figure(figsize=(14,10))
fig.suptitle('评论数top20的歌曲')
new_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728',
              '#9467bd', '#8c564b', '#e377c2', '#7f7f7f',
              '#bcbd22', '#17becf']
ax1 = fig.add_subplot(1,1,1)
ax1.bar(range(len(fig_data)),fig_data['total'].values, tick_label=fig_data.index, color=new_colors)
#xticks=list(range(0,len(line),int(len(line)/30)))  # 显示30个labels
#ax1.set_xticks(xticks.index.tolist())
for tick in ax1.get_xticklabels():
    tick.set_rotation(90)
ax1.set_xlabel('歌名')
ax1.set_ylabel('评论数量')
fig.show()
fig.savefig('output\\评论数top20的歌曲.jpg')

结果如下:

接下来按照评论数量为热度对所有歌手制作词云,此处用到第三方库wordcloud,在本文开头我们已经引入过了,安装方法:

pip install wordcloud

在这个例子中,我们选择一张佩奇照片作为词云最终的形状,在之后的例子中则以常规的正方形词云为主。

word_counts = total_comm_by_singer['total'].to_dict()

mask = np.array(Image.open('timg.jpg')) # 定义词频背景
wc = wordcloud.WordCloud(
    font_path='C:/Windows/Fonts/simhei.ttf', # 设置字体格式
    mask=mask, # 设置背景图
    max_words=200, # 最多显示词数
    max_font_size=100 # 字体最大值
)
wc.generate_from_frequencies(word_counts) # 从字典生成词云
image_colors = wordcloud.ImageColorGenerator(mask) # 从背景图建立颜色方案
wc.recolor(color_func=image_colors) # 将词云颜色设置为背景图方案
#plt.imshow(wc) # 显示词云
##plt.axis('off') # 关闭坐标轴
#plt.show() # 显示图像
wc.to_file("output\\所有歌手按照评论数量为热度制作词云.png")

其中,wc.generate_from_frequencies(word_counts) 接收的参数可以是字典dict,也可以是tuple形式,每一个元素都要有2个值,分别是词和词频。后面的例子中我们会用到collections库中的Counter方法,直接统计一个词汇列表中的每个词的词频。

词云的生成结果如图:

没想到薛之谦竟然还是第一。。。属他的名字最大!

接下来,以另外一个口径来绘制词云——所有歌手按照产量为热度绘制,在接下来的例子中,我们不再设置底图,直接生成400*400的图片:

tmp = total_songnum_by_singer['total'].to_dict()
word_counts = tmp
wc = wordcloud.WordCloud(
    font_path='C:/Windows/Fonts/simhei.ttf', # 设置字体格式
    #mask=mask, # 设置背景图
    background_color="white", #背景颜色
    max_words=200, # 最多显示词数
    max_font_size=100, # 字体最大值
    width=400,  #图幅宽度
    height=400
)
wc.generate_from_frequencies(word_counts) # 从字典生成词云
#wc.generate(word_counts)
wc.to_file("output\\所有歌手按照歌曲数量为热度制作词云.png")

结果:

什么情况?英语听力?!!!宝宝巴士??!!!好吧,看来学英语的、宝宝妈妈还是很多的啊,所以创造了N多的歌单

这不是我们想要的。怎么办呢?我们只需要屏蔽这些不符合预期的词汇后重新绘制就好了,因为用的是字典,直接使用pop方法把宝宝巴士、英语听力、群星都剔除掉,绘制代码还是用上面的即可。

tmp.pop('宝宝巴士')
tmp.pop('英语听力')
tmp.pop('群星')

结果如下:

岳云鹏第一!使用网易云音乐的三大主体是——————

1、学英语的。

2、宝宝妈妈/爸爸

3、德云社的真粉丝和塑料粉丝!小岳岳的名气可以的。

 

最后,我们对所有的评论信息分词制作词云。要分词,推荐很牛逼的中文分词库——jieba,安装方法还是pip install jieba。

import jieba

# 把所有评论以段落形式拼接
alltext = '\n'.join(all_data2['content'].tolist())
#segs=jieba.cut(alltext)
# 文本分词
seg_list_exact = jieba.cut(alltext, cut_all = False) # 精确模式分词
seg_list_exact2 = list(seg_list_exact)

解释一下上面的代码,在前面我把所有的信息都放到了all_data2中,评论在列content中,在这里呢把他以一个评论一段的形式拼接为一个大的字符串——alltext。

然后使用结巴分词jieba.cut()即可,cut会返回一个可迭代对象,方便大家循环迭代,写教程时我为了方便做多次测试,直接将其转为了一个list。注意:这个alltext差不多得有上千万字了,电脑单核比较弱的话做分词的时候会有点慢,耐心等待!

停顿词

说到停顿词,有人不知道什么意思或者意义何在,在对一篇文章或者一段文字分词时,文章中不可避免的存在大量的——标点符号、“你”,“我”,‘但是’,‘好像’,‘的’,‘了’,‘吗’,‘所以’,这些在词语中都是没有意义的辅助词,而他们的词频往往非常高,这就尴尬了,比如未处理时生成的下面这个词云图:

 

接下来处理停顿词,停顿词文件是myStopWords.txt,每行一个停顿词,读取文件后以换行符'\n'切割即可。将不再停顿词列表中的词汇添加到新的列表中。

object_list = []
with open('myStopWords.txt', 'r') as f:
    stopwords = f.read()
stopwords = stopwords.split('\n')   
for word in seg_list_exact2: # 循环读出每个分词
    if word not in stopwords: # 如果不在去除词库中
        object_list.append(word) # 分词追加到列表

分词清洗干净了,接下来collections.Counter统计词频,其参数传入列表即可(前文中有提及)

word_counts = collections.Counter(object_list) # 对分词做词频统计
wc = wordcloud.WordCloud(
    font_path='C:/Windows/Fonts/simhei.ttf', # 设置字体格式
    #mask=mask, # 设置背景图
    background_color="white", #背景颜色
    max_words=200, # 最多显示词数
    max_font_size=100, # 字体最大值
    width=400,  #图幅宽度
    height=400
)
wc.generate_from_frequencies(word_counts) # 从字典生成词云
#wc.generate(word_counts)
wc.to_file("output\\所有评论词云.png")

 

结果:

整个网易云音乐歌曲信息和评论的爬虫教程、数据分析教程到此结束。


欢迎关注公众号:python_trader,最新爬虫资源/教程、量化交易资源/教程都放在上面。

本系列爬虫教程源码,请在公众号回复爬虫即可获得。

转载请注明:Python量化投资 » python爬虫实例二——网易云音乐歌曲和评论的可视化数据分析

喜欢 (8)or分享 (0)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(3)个小伙伴在吐槽
  1. 你好,那个songments 的定义文件能发一下吗?
    hby2019-04-27 03:54 回复
    • 没有这个文件吧。所有我都放出来了。
      kingmo8882019-04-28 09:36 回复
  2. 请问song_infos那个表是怎么导出的,本地没有这个
    OnLai2019-06-06 04:42 回复