1、任务目标
① 爬五月天正规专辑的所有歌曲的歌词
② 按照年份进行情感分析,并制作词云
2、方法
在qq音乐上用Python进行爬虫,总共499首歌曲,其中包括演唱会和电影原声带等,需要进行数据清洗。
3、情感语义分析
弯路部分:
原本是想用Beautiful Soup 来抓歌词,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据,但是后来发现此路不通。
其一,因为qq音乐里的url是固定不变的,并不是每一页是一个单独的url。点击下一页,这个url永远在那里不变,摔!!
image.png
打开之后跳转到的是歌手的首页,并不是我们要的单曲页。
2.png
因为我是完全没有学过php,后来搜索了一圈才知道,好像这个翻页是用js写的(还是什么ajax?),就是实际上看不到的……反正我是找了好久也没找到那个对应499首歌曲的对应页面的url。
其二,因为Beautiful Soup要依赖标签定位,而可以看到有的歌曲有mv注释,有的歌曲有热门注释,所以源码里标签并不是非常一致。
因为是小白,这个地方也摸索了半天,后来才发现怎么也提取不到《我不愿 让你一个人》的歌名,才知道是标签xx.[1]这种写法有问题,其他歌曲都是通过xx.[1]就可以定位,而这首歌由于没有mv,所以要提取歌名的话,对应标签是xx.[0],写半天却发现此路不通。
综上,后来分析了一下,就是由于自己对php太小白,啥也不懂,所以才浪费了这么久。还有就是,其实刚开始上手某种语言时,都会经历这种比较爬坡绕路的时期,不要怕,坚持一下,多搜索下前人经验,总会有办法的。还好我没放弃~~~
好吧,那我们开始寻找另一条路。
然后我不甘心,把所有能打开的url都打开了一遍,看看都是些什么鬼。
最后让我找到了这个网址:
https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?g_tk=5381&jsonpCallback=MusicJsonCallbacksinger_track&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0&singermid=000Sp0Bz4JXH0o&order=listen&begin=0&num=30&songstatus=1
打开是酱紫的:
image.png
小小兴奋一下,仔细看里面有专辑名、歌名、歌手名,甚至还有歌词的id。
image.png
到这里就有思路了,可以用正则表达式把歌名、专辑名、歌词链接等关键字都筛选出来就好了呀~
突然有种柳暗花明的感觉呢!
但是这仅仅是499首单曲里第一页的内容,其他页呢?
通过观察发现,在url里这里很不一样!
https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?g_tk=5381&jsonpCallback=MusicJsonCallbacksinger_track&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0&singermid=000Sp0Bz4JXH0o&order=listen&begin=0&num=30&songstatus=1
begin=0&num=30决定了每页第一首歌曲的序号及歌曲数目。
只要做个循环就可以翻页了都爬下来了嘛~
代码走起,先来看看最基本的,爬了第一页的歌曲:
import re
import pandas as pd
import requests
response = requests.get("https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?g_tk=5381&jsonpCallback=MusicJsonCallbacksinger_track&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0&singermid=000Sp0Bz4JXH0o&order=listen&begin=0&num=30&songstatus=1")
response = response.text
print(response)
songname = '"songname":"(.?)","songorig"'
res1 = re.findall(songname,response)
for i in res1:
print(i)
image.png
同理,爬了专辑名、歌词还有一个musicid后面会用到。
然后只要将每一页的歌曲都抓出来就好了,用一个循环增加页码即可。
import re
import requests
for i in range(0,481,30):
response = requests.get("https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?g_tk=5381&jsonpCallback=MusicJsonCallbacksinger_track&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0&singermid=000Sp0Bz4JXH0o&order=listen&begin="+str(i)+"&num=30&songstatus=1")
response = response.text
#print(response)
songname = '"songname":"(.?)","songorig"'
album = '"albumname":"(.?)","alertid"'
strMedia = '"strMediaMid":"(.?)","stream"'
musicid = '"songid":(.*?),"songmid"'
res1 = re.findall(songname,response)
res2 = re.findall(album,response)
res3 = re.findall(strMedia,response)
res4 = re.findall(musicid,response)
for i in range(0,len(res1)):
if res2 == '' or res3 == '' or res4 == '' :
continue
print (str(i),'\t',res1[i],'\t',res2[i],'\t',"//y.qq.com/n/yqq/song/"+res3[i]+".html",'\t',res4[i]) image.png
这里最开始print的for循环写的是:
for i in range(0,30)
拉到最后报错说list index out of range
但代码改成for i in range(0,len(res1))之后还是有问题,实际抓取出来只有409首歌,剩下的3页90首歌因为有的专辑名为空,会同样出现list index out of range的问题,只是不报错,而是直接跳过去继续打印下一页的内容了。
后来加上if语句,将为空的元素跳过继续执行循环就好了。
关于数组有时候很容易懵b。但还是那句话,刚开始学,在还没有那么扎实的理论功底时,这样边试错、边实战是对代码理解最深刻、学得最快的方式。然后在实操之后,可以再反过来,重新看一下基础的理论知识,巩固一下基本功,此时自己的理解力肯定跟两眼一抹黑,从头学的时候不一样了,理解会更深刻的。
接下来是导出到excel中,然后根据歌词的链接,再去抓取歌词。最后就可以对歌词进行分析了~
我是直接把print出来的内容copy到txt文档中,再copy到excel即可,怎么快怎么来。
按照专辑分类,将演唱会、日语专辑等删除,按照年份对原始先做一次清理。
最后整理完一共是124首歌。
然后找到歌词页,继续解析网页源码,结果发现源码里并没有直接给出歌词,shit!
image.png
完全陷入困境,不知从何下手。
然后搜了好多大神的方法,让我找到了一位大神的github:
(https://github.com/lhtlht/qqmusic)
潜心研究了快一天啊!
最后稍微看明白一些,原来能够有效区分不同歌曲的key有两个,一个是在url中的歌曲号musicid(之前没注意,没有爬下来,后来加上的),一个是在referer里用到的歌词号songmid(也就是我们之前抓到的strMedia)
image.png
image.png
然后对大神的代码做了调整,改了好久啊,才跑成功,更改如下:
(简书的编辑器会吞空格……看我公众号的原版吧,无奈)
#抓取歌词函数
import requests
import json
import pandas as pd
import re
def getLyric(musicid,songmid):
url = 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric.fcg?nobase64=1&musicid='+musicid+'&callback=jsonp1&g_tk=5381&jsonpCallback=jsonp1&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0'
header = {
'user-agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
'referer':'https://y.qq.com/n/yqq/song/{}.html'.format(songmid)
}
paramters = {
'nobase64':'1',
'callback':'jsonp1',
'g_tk':'5381',
'jsonpCallback':'jsonp1',
'loginUin':'0',
'hostUin':'0',
'format':'jsonp',
'inCharset':'utf8',
'outCharset':'utf-8',
'notice':'0',
'platform':'yqq',
'needNewCode':'0'
}
html = requests.get(url=url,params=paramters,headers=header)
res = json.loads(html.text.lstrip('jsonp1(').rstrip(')'))
#由于一部分歌曲是没有上传歌词,因此没有默认为空
lyric = json.loads(html.text.lstrip('jsonp1(').rstrip(')'))['lyric']
#对歌词内容做稍微清洗
dr1 = re.compile(r'&#\d.;',re.S)
dr2 = re.compile(r'[\d+]',re.S)
dd = dr1.sub(r'',lyric)
dd = dr2.sub(r'\n',dd).replace('\n\n','\n')
return dd
带入一首《志明与春娇》的musicid和 songmid试试看:
lyric = getLyric('4830147','001Sh6UI3dh9mE')
print (lyric)
image.png
写到这里,快哭了好吗!!!
然后把大神对于歌词格式的更改稍微改了一下,便于导出。
从excel中导入musicid 和 songmid。
#抓取歌词函数 getLyric 及从excel传参
import requests
import json
import pandas as pd
import re
import openpyxl
wb = openpyxl.load_workbook(r'D:\lyric.xlsx') #打开excel文件
#print(wb.get_sheet_names()) #获取工作簿所有工作表名
sheet = wb.get_sheet_by_name('清洗') #获取工作表
#print(sheet.title) #获取sheet页的名称
def getLyric(musicid,songmid):
url = 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric.fcg?nobase64=1&musicid='+musicid+'&callback=jsonp1&g_tk=5381&jsonpCallback=jsonp1&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0'
header = {
'user-agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
'referer':'https://y.qq.com/n/yqq/song/{}.html'.format(songmid)
}
paramters = {
'nobase64':'1',
'callback':'jsonp1',
'g_tk':'5381',
'jsonpCallback':'jsonp1',
'loginUin':'0',
'hostUin':'0',
'format':'jsonp',
'inCharset':'utf8',
'outCharset':'utf-8',
'notice':'0',
'platform':'yqq',
'needNewCode':'0'
}
html = requests.get(url=url,params=paramters,headers=header)
res = json.loads(html.text.lstrip('jsonp1(').rstrip(')'))
#由于一部分歌曲是没有上传歌词,因此没有默认为空
lyric = json.loads(html.text.lstrip('jsonp1(').rstrip(')'))['lyric']
#对歌词内容做稍微清洗
dr1 = re.compile(r'&#\d.;',re.S)
dr2 = re.compile(r'[\d+]',re.S)
dd = dr1.sub(r'',lyric)
dd = dr2.sub(r'\n',dd).replace('\n',',')
return dd
lyric_list = []
for i in range(2,126,1):
lyric_list = getLyric(str(sheet.cell(row=i,column=5).value),str(sheet.cell(row=i,column=4).value))
print (lyric_list,'\n')
image.png
同上,copy到txt再直接粘贴到excel就好了,至此,歌词爬虫总算搞定了~
撒花~~~
image.png
这篇是比较理论的部分,下一篇会进行歌词的分析,结合年份看看我们主唱大人这几年的心-路-历-程,毕竟小伙子也变成发福中年大叔了嘛~哈哈
谢谢大家!
然后本人再次强调,小白Python选手,记性不好,单纯为个人记录之用。
本文可分享,未经允许请勿转载!
整理后的excel文档如果有人想用来分析,请在公众号(fancyfanyc)后台回复mayday即可获得下载链接。
请大神们多多指教,轻拍,喵~
江湖之大,后会有期,咱们下篇见~
网友评论