赞
踩
最近,博主喜欢上了听歌,但是又苦于找不到好音乐,于是就打算到网易云的歌单中逛逛
本着 “用技术改变生活” 的想法,于是便想着写一个爬虫爬取网易云的歌单,并按播放量自动进行排序
这篇文章,我们就来讲讲怎样爬取网易云歌单,并将歌单按播放量进行排序,下面先上效果图
打开 网易云音乐 歌单首页,不难发现这是一个静态网页,而且格式很有规律,爬取起来应该十分简单
按照以前的套路,很快就可以写完代码,无非就是分为下面几个部分:
这里我们使用 requests 发送和接收请求,核心代码如下:
import requests
def get_page(url):
# 构造请求头部
headers = {
'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
# 发送请求,获得响应
response = requests.get(url=url,headers=headers)
# 获取响应内容
html = response.text
return html
解析数据部分我们使用 xpath(对于 xpath 的语法不太熟悉的朋友,可以看看博主之前的文章)
核心代码如下:
from lxml import etree # 解析网页源代码,获取数据 def parse4data(self,html): html_elem = etree.HTML(html) # 播放量 play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()') # 歌单名称 song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title') # 歌单链接 song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href') song_link = ['https://music.163.com/#'+item for item in song_href] # 用户名称 user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title') # 用户链接 user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href') user_link = ['https://music.163.com/#'+item for item in user_href] # 将数据打包成列表,其中列表中的每一个元素是一个字典,每一个字典对应一份歌单信息 data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌单名称':b,'歌单链接':c,'用户名称':d,'用户链接':e},play_num,song_title,song_link,user_title,user_link)) # 返回数据 return data # 解析网页源代码,获取下一页链接 def parse4link(self,html): html_elem = etree.HTML(html) # 下一页链接 href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href') # 如果为空,则返回 None;如果不为空,则返回链接地址 if not href: return None else: return 'https://music.163.com/#' + href[0]
import requests from lxml import etree import json import time import random class Netease_spider: # 初始化数据 def __init__(self): self.originURL = 'https://music.163.com/#/discover/playlist' self.data = list() # 获取网页源代码 def get_page(self,url): headers = { 'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36' } response = requests.get(url=url,headers=headers) html = response.text return html # 解析网页源代码,获取数据 def parse4data(self,html): html_elem = etree.HTML(html) play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()') song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title') song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href') song_link = ['https://music.163.com/#'+item for item in song_href] user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title') user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href') user_link = ['https://music.163.com/#'+item for item in user_href] data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌单名称':b,'歌单链接':c,'用户名称':d,'用户链接':e},play_num,song_title,song_link,user_title,user_link)) return data # 解析网页源代码,获取下一页链接 def parse4link(self,html): html_elem = etree.HTML(html) href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href') if not href: return None else: return 'https://music.163.com/#' + href[0] # 开始爬取网页 def crawl(self): # 爬取数据 print('爬取数据') html = self.get_page(self.originURL) data = self.parse4data(html) self.data.extend(data) link = self.parse4link(html) while(link): html = self.get_page(link) data = self.parse4data(html) self.data.extend(data) link = self.parse4link(html) time.sleep(random.random()) # 处理数据,按播放量进行排序 print('处理数据') data_after_sort = sorted(self.data,key=lambda item:int(item['播放量'].replace('万','0000')),reverse=True) # 写入文件 print('写入文件') with open('netease.json','w',encoding='utf-8') as f: for item in data_after_sort: json.dump(item,f,ensure_ascii=False) if __name__ == '__main__': spider = Netease_spider() spider.crawl() print('Finished')
然而,事情真的有这么简单吗?
当我们运行上面的代码的时候,会发现解析网页源代码部分,返回的竟然是空列表!
这是为什么呢?敲重点,敲重点,敲重点,这绝对是一个坑啊!
我们重新打开浏览器,认真观察网页的源代码
原来,我们所提取的元素被包含在 <iframe> 标签内部,这样我们是无法直接进行定位的
因为 iframe 会在原有页面中加载另一个页面,当我们需要获取内嵌页面的元素时,需要先切换到 iframe 中
明白了原理之后,重新修改一下上面的代码
思路是利用 selenium 获取原有网页,再使用 switch_to.frame()
方法切换到 iframe 中,返回内嵌网页
需要修改的地方是获取网页源代码的函数,另外也需要在初始化数据的函数中实例化 webdriver,完整代码如下:
from selenium import webdriver from lxml import etree import json import time import random class Netease_spider: # 初始化数据(需要修改) def __init__(self): # 无头启动 selenium opt = webdriver.chrome.options.Options() opt.set_headless() self.browser = webdriver.Chrome(chrome_options=opt) self.originURL = 'https://music.163.com/#/discover/playlist' self.data = list() # 获取网页源代码(需要修改) def get_page(self,url): self.browser.get(url) self.browser.switch_to.frame('g_iframe') html = self.browser.page_source return html # 解析网页源代码,获取数据 def parse4data(self,html): html_elem = etree.HTML(html) play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()') song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title') song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href') song_link = ['https://music.163.com/#'+item for item in song_href] user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title') user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href') user_link = ['https://music.163.com/#'+item for item in user_href] data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌单名称':b,'歌单链接':c,'用户名称':d,'用户链接':e},play_num,song_title,song_link,user_title,user_link)) return data # 解析网页源代码,获取下一页链接 def parse4link(self,html): html_elem = etree.HTML(html) href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href') if not href: return None else: return 'https://music.163.com/#' + href[0] # 开始爬取网页 def crawl(self): # 爬取数据 print('爬取数据') html = self.get_page(self.originURL) data = self.parse4data(html) self.data.extend(data) link = self.parse4link(html) while(link): html = self.get_page(link) data = self.parse4data(html) self.data.extend(data) link = self.parse4link(html) time.sleep(random.random()) # 处理数据,按播放量进行排序 print('处理数据') data_after_sort = sorted(self.data,key=lambda item:int(item['播放量'].replace('万','0000')),reverse=True) # 写入文件 print('写入文件') with open('netease.json','w',encoding='utf-8') as f: for item in data_after_sort: json.dump(item,f,ensure_ascii=False) if __name__ == '__main__': spider = Netease_spider() spider.crawl() print('Finished')
这样,得到目前网易云音乐中播放量排名前十的歌单如下(哈哈,又可以愉快的听歌啦):
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。