参考这篇文章:python爬取歌词并生成词云图 - qq_39317214的博客 - CSDN博客
注:目前爬取到的歌词是歌手的TOP50首歌,如果要抓全部的,还需要改进
已完成:爬取歌词--》词频分析--》画词云
未来需要加上:
不以文本文件的形式储存,而是存到数据库里读取来操作;
不止TOP50;
同时抓取所属专辑信息等,之后可按专辑、年份来看意象、情感极性的变化,≈专辑主题风格;
对爬取到的歌词预过滤,去除音乐制作人、乐手信息;-----done,190207
判断是否是Live歌曲,以免重复计算;
其中主要遇到过的问题有:
1、编码解码
在file.write(str(text))时报错:
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
原因是Python自然调用ascii编码解码程序去处理字符流,当字符流不属于ascii范围内,就会抛出异常(ordinal not in range(128))。所以解决方法就是修改默认编码,需要注意的是需要先调用reload方法。
解决办法:
在开头加上:
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
2、歌名中包含了句点"?"等非法字符,导致以歌名作为文件名来创建文本文件key.txt时出现问题无法成功创建:
比如张悬有一首歌是“So?!...”,在运行时就会报错:
file = open(key+'.txt', 'a')
IOError: [Errno 22] invalid mode ('a') or filename: u'So?!....txt'
此时要将歌名中的这些非法字符去掉,比较好的方法是替换为下划线_ ,参考Python中过滤Windows文件名中的非法字符 - Zerokas的博客 - CSDN博客 写了以下替换函数:
#函数:当歌名中包含'?'等非法字符时无法创建文件为.txt,对这类歌名将非法字符替换成下划线_
def correct_filename(song_name):
pattern = r'[\\/:*?"<>|\r\n]+'
new_song_name = re.sub(pattern, "_", song_name)
return new_song_name
2.1、unicode字符串转换成string字符串(文件名可以用.结尾,所以不改也没事)
这里仍然涉及到编码问题,代码里一开始爬取到的歌名(key)是Unicode字符串,在判断是否是以“.”句点结尾时需要转换成string字符串,因此用到了key.encode('utf-8')。(因为我的代码文件是用utf-8编码的)
参考:Python2.X如何将Unicode中文字符串转换成 string字符串 - qiao1234 - 博客园
2.2、去停用词时的转码
但是在去停用词时又遇到了编码的问题(所以应该换用py3.x啊这个bug真的太难受了!):
UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
if word not in stopwords:
因为我一开始导入停用词的代码是:
def stopwordslist(filepath):
stopwords = [line.strip() for line in open(filepath, 'r').readlines()]
return stopwords
只要在这一步将词转换成unicde编码就可以进行之后的判断词是否在停用词表中了:
stopwords = [line.strip().decode("gbk") for line in open(filepath, 'r').readlines()]
3、制作词云过程中会遇到的小问题
3.1、字体
一开始的结果是这样的:
还以为是编码的问题没搞定造成的,但是看了下中间过程中生成的字典,已经是正确的了,所以只有另一个可能:字体。
由于歌词大部分是中文,因此要选择支持中文的字体,比如当我换成另一个支持中文的字体后,生成的图片中的字就正常了:
但是马上又有了2个新的问题:词的分布不是按照我选用的图片来的(我选的是一张人像照,而图片里词布满了整个画布),以及,词云中有大量类似“编曲”“作词”“录音室”这种非真正歌词内容的词,反而处在最高频率列表上,所以……并不是网上摘一段代码就可以完事了啊(╯‵□′)╯︵┻━┻
3.2、在分词前先剔除歌词中音乐制作信息的内容
观察之前得到的词云结果,可以发现有相当多的音乐制作人信息分布在高频词频表上,如何将这一部分词剔除掉呢?这里有两个方法:
方法1、在去停用词后,针对抓取到的歌词特性,再过滤一遍分词文本
通过观察爬取到的歌词,新生成了一个歌词过滤文本,对去停用词后的分词文本进行过滤,得到的词云就干净了很多,能够展现出重点了:
但是这需要不时更新这个歌词过滤文本,加入更多的词曲作者、编曲者、音乐人等人的名字,需要不断丰富。
方法2、在去停用词前就对爬取到的歌词做一判断,剔除音乐制作信息的部分,仅保留纯歌词部分
可以发现,爬取到的歌词前几行是音乐制作人 的信息,格式为“xxx:xxx”,可以从冒号来判断,剔除一行歌词中包含中文或英文冒号的部分,之后再进行分词、去停用词。这会产生一个新的问题,即真正歌词中包含冒号的内容也会被剔除,考虑到这部分歌词存在的概率很小,即使存在,对词频表的结果影响也很小,因此暂时忽略不计。附上这一方法的代码:
def filter_producers(lyrics):#过滤歌词中制作人信息
lyrics_filter_producers = ''
for line in lyrics:
if (line.find(':') != -1) | (line.find(':') != -1):#查找“歌词”中包含中英文冒号的语句,string.find()返回-1表示找不到
pass
else:
lyrics_filter_producers += line
lyrics_filter_producers += '\n'
return lyrics_filter_producers
3.3、让词云按照想要的图片来生成
词云没有按照我想要的图片的轮廓来生成的原因,是我采用的图片背景太过复杂、有别的颜色,所以wordcloud没有办法判断,参考生成词云之python中WordCloud包的用法 - 飞翔射手座 - CSDN博客的方法,选取背景是全白的图片,或者我们对图片做一些改动,让它的背景变成全白:
mask : nd-array or None (default=None) //如果参数为空,则使用二维遮罩绘制词云。如果 mask 非空,设置的宽高值将被忽略,遮罩形状被 mask 取代。除全白(#FFFFFF)的部分将不会绘制,其余部分会用于绘制词云。如:bg_pic = imread('读取一张图片.png'),背景图片的画布一定要设置为白色(#FFFFFF),然后显示的形状为不是白色的其他颜色。可以用ps工具将自己要显示的形状复制到一个纯白色的画布上再保存,就ok了。
为了方便,我直接用了PPT里的“删除背景”的功能(在PPT中,怎么删除图片的背景(抠图)_百度经验),将图片处理成全白后生成了田馥甄的TOP50歌曲的歌词词云:
简单概括一下,田馥甄的歌曲主要表达的就是:“就算没有人陪,就算自己很寂寞,也要拥抱眼泪,遗忘所有伤,谢谢这个世界,相信自己还是可以有爱和快乐的”,嗯真的是很倔强的歌手呢:)