网易云热歌都唱了什么?记一次python练习

关键词:数据可视化、数据分析、Python爬虫

最近在学python,虽然还只是一点皮毛,但终于可以做做自己的小项目了。学习任何一门语言的最好方法就是实践,能完成一个小项目,还是很有成就感的。

这次我想知道,网易云的热门歌曲都唱了什么。

想要获得热歌,我的第一个想法是可以爬爬网易歌单,幸运的是,网易云有一个歌单(云音乐热歌榜)专门收录当下的热门歌曲。

而想知道唱了什么,则可以从歌词入手,在歌单页面下有一个列表,每一个歌名都有链接,可以通过循环的方式获取每一个歌曲页面,并想办法获得歌词信息。因为无法直接在歌曲页面上爬取歌词,就需要寻找网易云的api,还好,这个也没花太久时间。需要注意的是,每一个歌曲都有唯一的id,可以通过这个id来指定歌词的链接。最后爬取的结果是一张表,有200首歌曲,涵盖的信息分别为id、歌名、链接、歌词,保存为csv到本地。

这个项目一共用到这些库:

BeautifulSoup、lxml、requests、wordcloud、pkuseg、matplotlib、re、pandas。

其中BeautifulSoup、lxml、re用于网页解析或字符匹配,requests用于获取网页,pandas用于数据处理,wordcloud库用于制作词云,pkuseg用于分词,matplotlib用于加载图像。

从上面的思路出发,我先写了一个爬虫脚本。

爬虫写好后,再写一个主程序,就可以运行爬虫,下载歌单信息。

保存的csv大概是这样的:

image-20200223150250918
image-20200223150250918

接下来写了数据可视化的程序。

最后用主程序把爬虫和数据分析链接起来。经过好一番调试,终于跑了起来。

以下是实验的结果,生成的词云均通过停词表过滤。

image-20200224212514172
image-20200224212514172

可以看到排名靠前的词中有非常多感染力强的的拟声词或语气词,这里列举一下:

doo、oh、ya、da、yeah

为啥da这个词会这么多呢?查看文件发现了这么一首歌:

image-20200224002730017
image-20200224002730017

...

这也许说明热门歌曲之所以热门,是因为其歌曲含有许多较为原始的发声吧,而这些发声虽然原始却很能引人注意,带动情绪。

那么除去了这些语气词后呢?

image-20200224212840916
image-20200224212840916

可以看到,“baby”和“世界”是出现最多的,其次还有“时间”、“爱情”、“也许”、“未来”、“离开”。

至于有什么含义,我也没有太好的想法,大家自由发挥吧,欢迎留言评论。

前20词汇排名:

前1-10次数前11-20次数
baby97love50
世界68time48
时间68night42
爱情64running40
也许58yeah40
未来58回忆39
ma56北京39
离开55想要39
move54body39
dancin'53да39

在完成上面的小项目后,我又不满足于此了。我想获得更多歌曲的信息。

通过网易云歌单搜索可以获得歌单列表,这是歌单的歌单。如搜索“摇滚”,将会得到成千上万条歌单结果,并且是按照收藏人气排名的。通过网易云api搜索歌单列表,得到一个保存歌单的表,再通过上文方法循环爬取数据。最终可得到上万首歌曲歌词信息,不过需要注意的是,同一首歌可能出现在不同歌单里,因此出现重复歌曲时选择跳过。

最后所有参数在settings.py里设置,以方便多测试几个类别。我终于可以知道“摇滚”、“民谣”、“说唱”都唱了什么了。

以下是我实验的结果。

摇滚

爬取了“摇滚”歌单搜索结果的前50个歌单(按收藏人气排名),一共有6215首歌,其中无歌词的歌曲有478首,因此有效数据为5737首。

image-20200224220812489
image-20200224220812489

去语气词后:

image-20200224221356112
image-20200224221356112

看来“时间”和“爱”是摇滚里出现最多的,此外还有“baby”、“回来”、“生活”、“感受”、“心”、“夜晚”,这是英文的语境。中文歌词不太明显,我们之后做进一步处理。从表中看去,“爱”的频次明显高于其他,不得不说,搞摇滚的对真的很执着。

前20词汇:

前1-10次数前11-20次数
love4926home1221
time3199eyes1214
baby2081世界1185
feel2072find1179
back2035long1152
life1894mind1140
heart1714good1113
day1570man1010
night1553run939
world1405tonight932

我想了解中文歌曲的情况,因此尝试在原有基础上去除英文词汇:

image-20200225013058240
image-20200225013058240

前1-10次数前11-20次数
世界1185希望358
ない695生命358
时间648地方356
って515自由340
离开505城市328
生活462明天305
永远432天空300
等待389这是294
未来388して284
忘记365孤独278

日文我看不懂,机器翻译又有失准确性,还是略过好了。

“时间”和“世界”排在前,其次是“离开”和“生活”。

我特地看了“离开”的语境,发现大多是描述人与人之间的离开:

image-20200225014309542
image-20200225014309542

而“生活”则是各种各样的,“生活”太丰富,这里太挤就不举例的。

他们在“等待”,查看歌词才发现绝大多数都是在等待着某人。

他们谈论了许多次“未来”,又害怕“忘记”自己,忘记了对方和方向。怀揣“希望”又或者只是“希望”着什么,身处“城市”里感受“孤独”。

他们关心“生命”,也像每一个摇滚的人一样,关心“天空”和“自由”,却对明天有着不一样的看法,有的乐观,有的充满疑惑。

image-20200225020144883
image-20200225020144883

民谣

爬取了“民谣”歌单搜索结果的前50个歌单(按收藏人气排名),一共有6506首歌,其中无歌词的歌曲有564首,因此有效数据为5942首。

未去语气词的词云:

image-20200225004811710
image-20200225004811710

过滤语气词后的词云:

image-20200225005756964
image-20200225005756964

前1-10次数前11-20次数
世界1760喜欢999
love1610永远992
时间1450远方938
姑娘1435孤独937
生活1316城市916
离开1066故事902
时光1054青春898
爱情1051也许886
再见1039地方856
回忆1024记得845

民谣歌里提到了最多次“世界”和“love”,“姑娘”居然能排在第四的位置,根据我直男的第一反应,唱民谣的肯定大都是大老爷们,而且还是很想要交女朋友的那种。

从结果中可以看出,与时间有关的词汇非常多,有“时间”、“时光”、“回忆”、“永远”、“岁月”、“记得”,在词云中还可以看到“忘记”、“日子”、“匆匆”、“消失”、“未来”、“从前”、“遗忘”、“忘记”。看来歌手对于时光也有一些执着,有的人想忘记,有的人却在努力回忆,他们很在乎“故事”。他们似乎有许多不太确定的想法,说了比别人多得多的“也许”。

表达离别的词汇“离开”和“再见”出现了很多次,后来他们心中有了“远方”、“城市”、“地方”、“南方”和“北方”,他们在“生活”,感受着情绪,有“快乐”也有“悲伤”,有时“微笑”,但也时常“沉默”,甚至一个人流“眼泪”。更多时候,只是“孤独”、“孤单”、“寂寞”,多么渴望“温柔”,渴望一个“拥抱”。

他们提到了很多很多次“爱情”、还有“喜欢”的人和东西。也许民谣就像这样,明明唱着歌,却好像和你聊了一次家常,讲了讲一些有趣或伤感的故事。

说唱

爬取了“说唱”歌单搜索结果的前50个歌单(按收藏人气排名),一共有11438首歌,其中无歌词的歌曲有7374首,有效数据为4064首。

未过滤语气词的词云:

image-20200225003346657
image-20200225003346657

看起来还好,就是不容易找到重点,那么过滤语气词之后呢?

image-20200225002013991
image-20200225002013991

“love”是最多的,其次是“baby”、“back”,无论中英文,也无论歌曲的种类,大家似乎都对“时间”很关心。“感觉”出场了,“man”和“girl”几乎一样多。

去除英文内容后的词云(虽然不是很干净):

image-20200225024053290
image-20200225024053290

前1-10次数前11-20次数
نى2136喜欢1112
ئا1825لى1112
مە1567兄弟1099
时间1392uh1076
世界1363قا1028
ىم1326سە1010
真的1270يا1010
想要1263ىن957
生活1176دى935
بى1119دە935

(尽量)去掉英文常用词后,出人意料的是有一些中东歌曲乱入了,大家忽略就好。中文里“时间”和“世界”仍然出现最多次,其次是“真的”,如何理解?先不慌想这个,我们看看其他词。

看看“想要”、“生活”、“喜欢”、“希望”、“不用”,还有“老子”、“兄弟”,以及那些密密麻麻卡在缝里的关键词,如“都会”、“只能”、“放弃”、“别人”、“习惯”、“不停”、“从不”、“从来”,让人觉得这是一群有些憋屈却仍然逞能的年轻人在唱着自己的主张,然而在说唱里,直接提到“爱情”却比民谣要少得多,“喜欢”却提到了很多次。

说回来“真的”这个有些奇妙的词,我去翻了翻歌词,结果是这样的:

image-20200225000534639
image-20200225000534639

个人理解,词“真的”在情绪较强烈、或表强调的时候使用,所以在说唱中为了起到强调的效果,就不知不觉地用多了,我真的是这样想的。

除了“真的”,就是“想要”了:

image-20200225001143614
image-20200225001143614

“想要”是一种明确表达欲望的说法,看来歌手“想要”让大家都知道他的想法,是一种主动的表现。

最后,我们来一次横向比较:

词云比较
词云比较

emmmm,看起来生活、时间、世界均稳居前列,不如我们把这三者加入停用词,看看之后会有什么结果。

摇滚
摇滚

民谣
民谣

说唱
说唱

你发现什么了吗?还请留言评论。

分词和词云的效果与文本清理、分词工具有关。文本清理要注意去掉多余的符号,还要去掉多余的空格,尤其是在生成词云之前。分词结果直接影响词云效果,要选择合适的分词工具,之前测试了jieba,始终效果不太好,后来改用北大开发的pkuseg,效果要好一些,分词性能也更高。

在写代码的过程中,有一些经验教训也总结总结吧。

关于爬虫:在获取歌词的函数中,见get_lyric(),要考虑到歌词不存在的情况,如果歌词不存在直接跳过,会造成csv表项目不匹配,导致尾部数据缺失。虽然考虑到了,若直接设为空值,后果则是默认属性将为{float}NaN,浮点数空值。歌词项应该统一为str,若中间穿插其他类型,将为后续造成许多麻烦。

关于Python:.remove().append

分词时,有一个过滤常用词的操作,开始时不懂,去掉常用词直接用的.remove(),后来才发现这严重影响性能,文本越大,性能影响越大。可以另定义一个列表,用.append()操作。我估计用.remove()操作时需要移动列表之后的每一个元素,复杂度直接指数增加了。

关于正则表达式:在使用正则表达式替换字符时,或运算不宜太多,不必要的或运算可以转为其他方式处理,比如无效词/字符可以放入停用词表处理。

关于使用停用词表:要特别注意英文单词有大小写形式,最好保证停词表统一为大写或小写,在词语匹配的时候分词要相应的用.upper().lower()形式。

关于工具使用:使用wordcloud库的时候没认真看文档,入了一个坑,使用.generate()生成的词云不一定按照词频处理,有些词频高的词语反而无法显示或显示较小,这是库算法自行决定的,若要按照词频生成词云,需要用.generate_from_frequencies()方法。可以用tabulate打印markdown表格。

代码已经放在Github上了,感兴趣的同学可以点击阅读全文查看。

https://github.com/OhiyoX/NeteaseMusic-Lyric-Analysis