python爬取网易云周杰伦所有专辑,歌曲,评论,并完成可视化分析
Posted zfyolo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python爬取网易云周杰伦所有专辑,歌曲,评论,并完成可视化分析相关的知识,希望对你有一定的参考价值。
这篇文章适合于python爱好者,里面可能很多语句是冗长的,甚至可能有一些尚未发现的BUG,这个伴随着我们继续学习来慢慢消解吧。接下来 我把里面会用到的东西在这里做一个简单总结吧:本文用到了两门解释性编程语言python3 + bash(shell),为什么用shell,我会在后面具体分析。用到的模块requests,re,os,jieba,glob,json,lxml,pyecharts,heapq,collections.看到这么多模块,大家一定很头痛,其实最开始我也没想到会用到这么多。不过随着程序的进行,这些模块自然的就出现在程序里,初学者对每一个模块没必要去特别了解。但是用法需要掌握。 话不多说,接下来就进入我们的正题吧。
一.找到需要爬取的内容,分析网页,抓包查看交互内容
首先我们先进入到我们需要抓取的内容的地址。http://music.163.com/# 这是网易云音乐的首页,我们的目的是抓取周杰伦的所有歌曲,歌词,已经评论,那我们在搜索处输入周杰伦得到这张图,我们发现这里面只有最多50首歌(很多人分析网易云的歌曲就只选取TOP50),我们想要的是全部,所以这个URL不符合要求,我们继续寻找其他的URL地址。我在这里花了不少时间,最后找到了一个间接的方法,首先抓取周杰伦的全部专辑信息,然后通过专辑信息再去寻找全部歌曲(目前在网易云上我还没发现什么方法可以直接获取全部歌曲名字)。好了确定好了方针,我们第一步抓取所有专辑 进入http://music.163.com/#/artist/album?id=6452如下图所示!在这里面我们可以看到周杰伦所有专辑信息点击下一页 观察url发现变成了 http://music.163.com/#/artist/album?id=6452&limit=12&offset=12 这样!!!所以有点html基础的人都知道这里的limit=12是每页显示专辑的数量。OK,接下来我们就来获取专辑吧!我们在页面输入http://music.163.com/#/artist/album?id=6452&limit=100&offset=12(改成100 避免多次抓取,一次抓去完),在谷歌的抓包工具(F12)里面查看交互信息发现如下:
是的你没看错,这就是我们想要的信息,那事情就变得简单的,我们没必要用复杂的工具比如(selenium)去加载整个页面,(事实上,如果还没想到抓取歌曲的方法,我估计就得用它了),我们再看header里面有什么这里面的string我们不用管了,因为它已经在我们的url里面了,我们只需要看request headers 这个就是我们给服务器发送的东西,发送之后,服务器返回给我们的就是network里面的信息。好,接下来我们伪造浏览器发送请求。具体代码如下:
def GetAlbum(self): urls="http://music.163.com/artist/album?id=6452&limit=100&offset=0" headers={ \'Accept\':\'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\', \'Accept-Encoding\':\'gzip, deflate, br\', \'Accept-Language\':\'zh-CN,zh;q=0.9\', \'Connection\':\'keep-alive\', \'Cookie\':\'_iuqxldmzr_=32; _ntes_nnid=dc7dbed33626ab3af002944fabe23bc4,1524151830800; _ntes_nuid=dc7dbed33626ab3af002944fabe23bc4; __utmc=94650624; __utmz=94650624.1524151831.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=94650624.1505452853.1524151831.1524151831.1524176140.2; WM_TID=RpKJQQ90pzUSYfuSWgFDY6QEK1Gb4Ulg; JSESSIONID-WYYY=ZBmSOShrk4UKH5K%5CVasEPuc0b%2Fq6m5eAE91jWCmD6UpdB2y4vbeazO%2FpQK%5CgiBW0MUDDWfB1EuNaV5c4wIJZ08hYQKDhpsHnDeMAgoz98dt%2B%2BFfhdiiNJw9Y9vRR5S4GU%2FziFp%2BliFX1QTJj%2BbaIGD3YxVzgumklAwJ0uBe%2FcGT6VeQW%3A1524179765762; __utmb=94650624.24.10.1524176140\', \'Host\':\'music.163.com\', \'Referer\':\'https://music.163.com/\', \'Upgrade-Insecure-Requests\':\'1\', \'User-Agent\':\'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\' } html = requests.get(urls,headers=headers) html1=etree.HTML(html.text) html_data=html1.xpath(\'//div[@class="u-cover u-cover-alb3"]\')[0] pattern = re.compile(r\'<div class="u-cover u-cover-alb3" title=(.*?)>\') items = re.findall(pattern, html.text) cal=0 # 首先删除这个文件,要不然每次都是追加 if(os.path.exists("专辑信息.txt")): os.remove("专辑信息.txt") #删除文件避免每次都要重复写入 if (os.path.exists("专辑歌曲信息.txt")): os.remove("专辑歌曲信息.txt") for i in items: cal+=1 #这里需要注意i是有双引号的,所以需要注意转换下 p=i.replace(\'"\',\'\') #这里在匹配里面使用了字符串,注意下 pattern1=re.compile(r\'<a href="/album\\?id=(.*?)" class="tit s-fc0">%s</a>\'%(p)) id1= re.findall(pattern1,html.text) # print("专辑的名字是:%s!!专辑的ID是%s:"%(i,items1)) with open("专辑信息.txt",\'a\') as f: f.write("专辑的名字是:%s!!专辑的ID是%s \\n:"%(i,id1)) f.close() self.GetLyric1(i,id1) # print("总数是%d"%(cal)) print("获取专辑以及专辑ID成功!!!!!")
这里面用到了xpath来找到对应标签里面数据,然后把数据放在文件里面。代码不重要,思想懂了就行(代码单独执行可行)
执行结果如下
二.抓取歌曲信息。
通过上面我们已经抓取到了专辑的信息,接下来我们就通过专辑,来获取歌曲信息
看这幅图,我想你已经懂了,页面组成http://music.163.com/#/album?id=!!! !!!这里填写专辑ID,我们在network里面找到了所有歌曲的信息接下来我们看header同样的道理我们通过伪造方式发送信息,获取歌曲信息!!直接上代码
def GetLyric1(self,album,id1): urls1 = "http://music.163.com/#/album?id=" urls2 = str(id1) urls3= urls1+urls2 #将不要需要的符号去掉 urls=urls3.replace("[","").replace("]","").replace("\'","").replace("#/","") headers={ \'Cookie\': \'_iuqxldmzr_=32; _ntes_nnid=dc7dbed33626ab3af002944fabe23bc4,1524151830800; _ntes_nuid=dc7dbed33626ab3af002944fabe23bc4; __utmz=94650624.1524151831.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=94650624.1505452853.1524151831.1524176140.1524296365.3; __utmc=94650624; WM_TID=RpKJQQ90pzUSYfuSWgFDY6QEK1Gb4Ulg; JSESSIONID-WYYY=7t6F3r9Uzy8uEXHPnVnWTXRP%5CSXg9U3%5CN8V5AROB6BIe%2B4ie5ch%2FPY8fc0WV%2BIA2ya%5CyY5HUBc6Pzh0D5cgpb6fUbRKMzMA%2BmIzzBcxPcEJE5voa%2FHA8H7TWUzvaIt%2FZnA%5CjVghKzoQXNM0bcm%2FBHkGwaOHAadGDnthIqngoYQsNKQQj%3A1524299905306; __utmb=94650624.21.10.1524296365\', \'Host\': \'music.163.com\', \'Referer\': \'http://music.163.com/\', \'Upgrade-Insecure-Requests\': \'1\', \'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\' } html = requests.get(urls, headers=headers) html1 = etree.HTML(html.text) # soup = BeautifulSoup(html1, \'html.parser\', from_encoding=\'utf-8\') # tags = soup.find_all(\'li\', class_="have-img") html_data = html1.xpath(\'//ul[@class="f-hide"]//a\') for i in html_data: #注意这个用法 html_data1=i.xpath(\'string(.)\') #获取歌曲的id html_data2=str(html_data1) pattern1=re.compile(r\'<li><a href="/song\\?id=(\\d+?)">%s</a></li>\'%(html_data2)) items = re.findall(pattern1,html.text) # print("歌曲的名称为: %s"%(html_data2)) # print("歌曲的id为: %s"%(items)) with open("专辑歌曲信息.txt", \'a\') as f: print(len(items)) if (len(items) > 0): f.write("歌曲的名字是: %s!!歌曲的ID是%s \\n" % (html_data2, items)) f.close() print("获取歌曲 %s 以及歌曲的ID %s写入文件成功"%(html_data2, items)) #http://music.163.com/#/song?id=185617 # if(len()) def GetLyric2(self): #首先删除原来的文件,避免重复写入 for i in glob.glob("*热评*"): os.remove(i) for i in glob.glob("*歌曲名*"): os.remove(i) #直接读取所有内容 file_object=open("专辑歌曲信息.txt",) list_of_line=file_object.readlines() aaa=1 namelist = "" for i in list_of_line: # 歌曲的名字是: 同一种调调!!歌曲的ID是[\'186020\'] pattern1 = re.compile(r\'歌曲的名字是: (.*?)!!歌曲的ID是\') pattern2 = re.compile(r\'歌曲的ID是\\[(.*?)\\]\') items1 = str(re.findall(pattern1, i)).replace("[","").replace("]","").replace("\'","") items2 = str(re.findall(pattern2, i)).replace("[","").replace("]","").replace(\'"\',"").replace("\'","") headers = { \'Request URL\': \'http://music.163.com/weapi/song/lyric?csrf_token=\', \'Request Method\': \'POST\', \'Status Code\': \'200 OK\', \'Remote Address\': \'59.111.160.195:80\', \'Referrer Policy\': \'no-referrer-when-downgrade\' } # http://music.163.com/api/song/lyric?id=186017&lv=1&kv=1&tv=-1 urls="http://music.163.com/api/song/lyric?"+"id="+str(items2)+\'&lv=1&kv=1&tv=-1\' # urls = "http://music.163.com/api/song/lyric?id=186018&lv=1&kv=1&tv=-1" #print(urls) html = requests.get(urls, headers=headers) json_obj = html.text j = json.loads(json_obj) try: lrc = j[\'lrc\'][\'lyric\'] pat = re.compile(r\'\\[.*\\]\') lrc = re.sub(pat,"",lrc) lrc = lrc.strip() print(lrc) lrc = str(lrc) with open("歌曲名-"+items1+".txt", \'w\',encoding=\'utf-8\') as f: f.write(lrc) aaa+=1 namelist=namelist + items1 + ".txt"+"," #调用获取评论方法,并且把热评写入文件 self.GetCmmons(items1,items2) except: print("歌曲有错误 %s !!"%(items1)) #读取所有文件,并且把所有的信息输入到一个文件里面去 # html1 = etree.HTML(html.text) print("歌曲一共爬取了%s首 "%(aaa)) print(namelist)
上面需要注意:xpath来获取需要的信息,利用正则来获取ID(其实有很多方法)
结果如下,
同样的方法!!我们打开一首歌曲一样的道理,我们分析network来获取我们需要的信息歌词,评论!!直接上代码
def GetCmmons(self,name,id): self.name=name self.id=id #删除原来的文件 避免重复爬取 # urls="http://music.163.com/weapi/v1/resource/comments/R_SO_4_415792918?csrf_token=" urls="http://music.163.com/api/v1/resource/comments/R_SO_4_"+str(id) headers={ \'Accept\': \'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\', \'Accept-Encoding\': \'gzip, deflate\', \'Accept-Language\': \'zh-CN,zh;q=0.9\', \'Cache-Control\': \'max-age=0\', \'Connection\': \'keep-alive\', \'Cookie\': \'_iuqxldmzr_=32; _ntes_nnid=dc7dbed33626ab3af002944fabe23bc4,1524151830800; _ntes_nuid=dc7dbed33626ab3af002944fabe23bc4; __utmz=94650624.1524151831.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); WM_TID=RpKJQQ90pzUSYfuSWgFDY6QEK1Gb4Ulg; JSESSIONID-WYYY=BgqSWBti98RpkHddEBZcxnxMIt4IdbCqXGc0SSxKwvRYlqbXDAApbgN%2FQWQ8vScdXfqw7adi2eFbe30tMZ13mIv9XOAv8bhrQYC6KRajksuYWVvTbv%2BOu5oCypc4ylh2Dk5R4TqHgRjjZgqFbaOF73cJlSck3lxcFot9jDmE9KWnF%2BCk%3A1524380724119; __utma=94650624.1505452853.1524151831.1524323163.1524378924.5; __utmc=94650624; __utmb=94650624.8.10.1524378924\', \'Host\': \'music.163.com\', \'Upgrade-Insecure-Requests\': \'1\', \'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\' } html = requests.get(urls,headers=headers) html.encoding= \'utf8\' # html_data = html1.xpath(\'//div[@class="u-cover u-cover-alb3"]\')[0] # pattern = re.compile(r\'<div class="u-cover u-cover-alb3" title=(.*?)>\') #items = re.findall(pattern, html.text) #print(html.text) #使用json格式化输出 json_obj = html.text j = json.loads(json_obj) i=j[\'hotComments\'] for uu in i: print username=uu["user"][\'nickname\'] likedCount1 = str(uu[\'likedCount\']) comments=uu[\'content\'] with open(name + "的热评hotComment" +".txt" , \'a+\',encoding=\'utf8\') as f: f.write("用户名是 "+username+"\\n") f.write("用户的评论是 "+comments+"\\n") f.write("被点赞的次数是 " + str(likedCount1) +"\\n") f.write("----------华丽的的分割线-------------"+"\\n") f.close()
上面需要注意的是:利用json获取需要的数据(至少比正则快点)
结果如下:
到这里!!我们已经完成了所有用数据的爬取,数据大体如下
三 数据分析,可视化
如果数据不利用,就如同一张白纸一样,毫无意义。接下来我们就对数据进行全面的分析
第一步,我们先进行歌词的分析,先把数据合并到一个文件里
def MergedFile(self): aaa=0 for i in glob.glob("*歌曲名*"): file_object = open(i,\'r\',encoding=\'UTF-8\') list_of_line = file_object.readlines() for p in list_of_line: if "作词" in p or "作曲" in p or "混音助理" in p or "混音师" in p or "录音师" in p or "执行制作" in p or "编曲" in p or "制作人" in p or "录音工程" in p or "录音室" in p or "混音录音室" in p or "混音工程" in p or "Programmer" in p or p == "\\n" or "和声" in p or "吉他" in p or "录音助理" in p or "陈任佑鼓" in p or "周杰伦" in p: aaa+=1 print(p) else: with open ("allLyric"+".txt","a",encoding=\'UTF-8\') as f : f.write(p) f.write("\\n") print(aaa) #合并歌曲 file1 = open(\'allLyric.txt\', \'r\', encoding=\'utf-8\') # 要去掉空行的文件 file2 = open(\'allLyric1.txt\', \'w\', encoding=\'utf-8\') # 生成没有空行的文件 try: for line in file1.readlines(): if line == \'\\n\': line = line.strip("\\n") file2.write(line) finally: file1.close() file2.close() print("合并歌词文件完成")
上面需要注意的是:我们合并数据的时候,可以选择性的删除一些无用数据(也就是上面那一大串for,麻瓜式的删除)
结果如下
OK 歌词清洗完成之后,我们对周杰伦歌曲进行情绪化分析,在进行情绪化分析的时候,有很多工具可以选择,我这边选择的是SnowNLP这个三方工具,具体代码如下:
def EmotionAnalysis(self): from snownlp import SnowNLP from pyecharts import Bar xzhou=[] yzhou=[] for i in glob.glob("*歌曲名*"): count=0 allsen=0 with open(i,\'r\', encoding=\'utf-8\') as fileHandel: fileList = fileHandel.readlines() for p in fileList: if "作词" in p or "作曲" in p or "鼓" in p or "混音师" in p or "录音师" in p or "执行制作" in p or "编曲" in p or "制作人" in p or "录音工程" in p or "录音室" in p or "混音录音室" in p or "混音工程" in p or "Programmer" in p or p == "\\n": pass else: s = SnowNLP(p) # print(s.sentences[0]) s1 = SnowNLP(s.sentences[0]) #print(type(s1)) count+=1 allsen+=s1.sentiments i=str(i) xzhou1 = i.split("-", 1)[1].split(".",1)[0] xzhou.append(xzhou1) avg=int(allsen)/count yzhou.append(avg) #print("%s这首歌的情绪为%s"%(i,avg)) fileHandel.close() bar = Bar("柱状图数据堆叠示例") bar.add("周杰伦歌曲情绪可视化", xzhou, yzhou, is_stack=True,xaxis_interval=0) bar.render(r"D:\\学习\\untitled4\\allpicture\\周杰伦歌曲情绪全部.html") #显示最好的前五首歌 import heapq yzhou1 = heapq.nlargest(10, yzhou) temp = map(yzhou.index, heapq.nlargest(10, yzhou)) temp = list(temp) xzhou1 = [] for i in temp: xzhou1.append(xzhou[i]) # 情绪前十首歌个图 bar = Bar("周杰伦歌曲情绪较好前十首歌") bar.add("周杰伦歌曲情绪可视化", xzhou1, yzhou1, is_stack=True) bar.render(r"D:\\学习\\untitled4\\allpicture\\周杰伦歌曲最积极情绪top10.html") #显示最差的十首歌 yzhou1 = heapq.nsmallest(10, yzhou) temp = map(yzhou.index, heapq.nsmallest(10, yzhou)) temp = list(temp) xzhou1 = [] for i in temp: xzhou1.append(xzhou[i]) # print(xzhou1) #print(yzhou1) # 情绪前十首歌个图 bar = Bar("周杰伦歌曲情绪较差前十首歌") bar.add("周杰伦歌曲情绪可视化",xzhou1, yzhou1,xaxis_interval=0,xzhou1_label_textsize=6) bar.render(以上是关于python爬取网易云周杰伦所有专辑,歌曲,评论,并完成可视化分析的主要内容,如果未能解决你的问题,请参考以下文章