一、选题背景
由于现在的音乐版权问题,很多音乐分布在各个平台的音乐播放器,而版权问题也使很多人非常的困扰,从而找不到音乐的资源。因此为帮助使用网易云的伙伴们,更好的找到各个平台的资源,听到更多自己喜欢的歌。
二、爬虫方案设计
1.实现功能
2.具体实现
1.搜索部分
3.下载歌曲
1.再次获取信息
2.下载
4.结语
三、实现功能
可以分别对歌名,歌手,歌单进行搜索,搜索歌曲和歌单会列出页面第一页所显示的所有歌曲或歌单及其id以供选择下载,搜索歌手会下载网易云音乐列表显示第一位歌手的所有热门歌曲。
四、具体实现
1.搜索部分
网易云音乐搜索界面为:https://music.163.com/#/search ,经过测试发现
对单曲搜索时链接为:https://music.163.com/#/search/m/?s={}&type=1 ,
对歌手搜索时链接为:https://music.163.com/#/search/m/?s={}&type=100 ,
对歌单搜索时链接为:https://music.163.com/#/search/m/?s={}&type=1000
其中大括号中内容为要搜索内容的名称,例如搜索年少有为,网址为:https://music.163.com/#/search/m/?s=年少有为&type=1 搜索后即出现此页面

选择selenium + chrome 获取js渲染后的页面源码,并通过xpath提取想要的信息
python代码:
1 import requests
2 from urllib import request 3 from lxml import etree 4 from selenium import webdriver 5 import platform 6 import os 7 import time 8 9 10 headers = { 11 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36', 12 'Host': 'music.163.com', 13 'Referer': 'https://music.163.com/' 14 }
通过检查元素发现在网页的这一部分存在歌曲链接和名字

通过selenium获得页面源码
1 def selenium_get_html(url):
2 """通过selenium获得页面源码"""
3 # 无界面启动chrome
4 options = webdriver.ChromeOptions() 5 options.add_argument( 6 'User-Agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36"') 7 options.add_argument('--headless') 8 driver = webdriver.Chrome(chrome_options=options) 9 driver.get(url) 10 # 歌曲信息在frame框架内,进入frame框架得到源码 11 driver.switch_to.frame('contentFrame') 12 return driver.page_source
同理,在搜索歌单和歌手时检查元素也会发现类似的情况

得到具体信息
1 # 提取歌曲名称,演唱者姓名,和歌曲id以供选择
2 def search_input_song(url):
3 """获取歌曲名字和id"""
4 html = selenium_get_html(url) 5 6 root = etree.HTML(html) 7 id = root.xpath('//div[@class="srchsongst"]//div[@class="td w0"]//div[@class="text"]/a[1]/@href') 8 artist = root.xpath('//div[@class="srchsongst"]//div[@class="td w1"]//div[@class="text"]/a[1]/text()') 9 name = root.xpath('//div[@class="srchsongst"]//div[@class="td w0"]//div[@class="text"]//b/@title') 10 11 id = [i.strip('/song?id==') for i in id] 12 return zip(name, artist, id) 13 14 # 歌手默认选择第一位,所以仅得到第一位歌手的id 15 def search_input_artist(url): 16 """获取歌手id""" 17 html = selenium_get_html(url) 18 19 root = etree.HTML(html) 20 id = root.xpath('//div[@class="u-cover u-cover-5"]/a[1]/@href') 21 22 return id[0].strip('/artist?id==') 23 24 # 提取歌单名称,和歌单id以供选择 25 def search_input_playlist(url): 26 """获取歌单名字和id""" 27 html = selenium_get_html(url) 28 29 root = etree.HTML(html) 30 id = root.xpath('//div[@class="u-cover u-cover-3"]/a/@href') 31 name = root.xpath('//div[@class="u-cover u-cover-3"]//span/@title') 32 33 id = [i.strip('/playlist?id==') for i in id] 34 return zip(name, id)
五、下载歌曲
1.再次获取信息
得到id信息后,就可以在交互界面提示用户选择id下载。对于单曲来说获取用户选择的id后与对应地址拼接即可进行下载,而对于歌单和歌手因为每一个单位中都存在多首歌曲,第一次仅仅获取了歌手或歌单的id,需要二次分析。我们通过刚刚获取的链接可以发现
单曲的地址为:https://music.163.com/song?id={}
歌手的地址为:https://music.163.com/artist?id={}
歌单的地址为:https://music.163.com/playlist?id={} ({}内为id)
这回我们进入歌手的页面,检查元素查询歌曲信息,因为获取的页面中真正的歌曲信息并不在我们检查时看到所在的位置,通过查看框架源代码发现真正的信息在这,歌单页面也是一样。
![]()
python代码:
1 # url为歌单或歌手的地址
2 def get_url(url):
3 """从歌单中获取歌曲链接"""
4 req = requests.get(url, headers=headers) 5 6 root = etree.HTML(req.text) 7 items = root.xpath('//ul[@class="f-hide"]//a') 8 print(items) 9 10 return items
2.下载
通过之前的爬取,我们已经获得了歌曲的id及名称,可以进行下载了
网易云音乐有一个下载外链:https://music.163.com/song/media/outer/url?id={}.mp3
大括号中内容为所下载音乐在网易云的id,所以通过该外链和相应id即可下载网易云音乐。
将id和外链进行拼接后发送get请求,获取get请求所得到页面请求头中’Location’内容,这里面存储的是真正的音乐地址,使用request模块的urlretrieve方法下载。
python代码:
1 # song_id为歌曲id, song_name为歌曲名称
2 def download_song(song_id, song_name):
3 """通过外链下载歌曲"""
4
5 url = 'https://music.163.com/song/media/outer/url?id={}.mp3'.format(song_id) 6 7 # get请求设置禁止网页跳转,即allow_redirects=False 8 req = requests.get(url, headers=headers, allow_redirects=False) 9 song_url = req.headers['Location'] 10 try: 11 # path在主函数中输入 12 request.urlretrieve(song_url, path + "/" + song_name + ".mp3") 13 print("{}--下载完成".format(song_name)) 14 except: 15 print("{}--下载失败".format(song_name))
3.完整代码
1 import requests
2
3 from urllib import request 4 from lxml import etree 5 from selenium import webdriver 6 7 8 import platform 9 import os 10 import time 11 12 13 headers = { 14 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36', 15 'Host': 'music.163.com', 16 'Referer': 'https://music.163.com/' 17 } 18 19 20 21 def get_url(url): 22 """从歌单中获取歌曲链接""" 23 req = requests.get(url, headers=headers) 24 root = etree.HTML(req.text) 25 items = root.xpath('//ul[@class="f-hide"]//a') 26 print(items) 27 return items 28 29 30 31 def download_song(song_id, song_name): 32 """通过外链下载歌曲""" 33 url = 'https://music.163.com/song/media/outer/url?id={}.mp3'.format(song_id) 34 35 req = requests.get(url, headers=headers, allow_redirects=False) 36 37 song_url = req.headers['Location'] 38 try: 39 request.urlretrieve(song_url, path + "/" + song_name + ".mp3") 40 print("{}--下载完成".format(song_name)) 41 except: 42 print("{}--下载失败".format(song_name)) 43 44 45 46 def download(items): 47 """全部歌曲下载""" 48 for item in items: 49 song_id = item.get('href').strip('/song?id=') 50 song_name = item.text 51 download_song(song_id, song_name) 52 print("-------下载完成-------") 53 54 55 def artist_id_down(id): 56 """根据歌手id下载全部歌曲""" 57 artist_url = 'https://music.163.com/artist?id={}'.format(id) 58 items = get_url(artist_url) 59 download(items) 60 61 62 def playlist_id_down(id): 63 """根据歌单id下载全部歌曲""" 64 playlist_url = 'https://music.163.com/playlist?id={}'.format(id) 65 items = get_url(playlist_url) 66 download(items) 67 68 69 def get_song_name(url): 70 """在歌曲页面获得名字""" 71 req = requests.get(url, headers=headers) 72 root = etree.HTML(req.text) 73 name = root.xpath('//em[@class="f-ff2"]/text()') 74 return name[0] 75 76 77 def song_id_down(id): 78 """根据歌曲id下载""" 79 url = 'https://music.163.com/song?id={}'.format(id) 80 name = get_song_name(url) 81 download_song(id, name) 82 83 84 def selenium_get_html(url): 85 """通过selenium获得页面源码""" 86 options = webdriver.ChromeOptions() 87 88 89 options.add_argument( 90 'User-Agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36"') 91 options.add_argument('--headless') 92 driver = webdriver.Chrome(chrome_options=options) 93 driver.get(url) 94 driver.switch_to.frame('contentFrame') 95 driver.close() 96 return driver.page_source 97 98 99 def search_input_song(url): 100 """获取歌曲名字和id""" 101 html = selenium_get_html(url) 102 root = etree.HTML(html) 103 104 id = root.xpath('//div[@class="srchsongst"]//div[@class="td w0"]//div[@class="text"]/a[1]/@href') 105 artist = root.xpath('//div[@class="srchsongst"]//div[@class="td w1"]//div[@class="text"]/a[1]/text()') 106 name = root.xpath('//div[@class="srchsongst"]//div[@class="td w0"]//div[@class="text"]//b/@title') 107 108 109 id = [i.strip('/song?id==') for i in id] 110 return zip(name, artist, id) 111 112 113 def search_input_artist(url): 114 """获取歌手id""" 115 html = selenium_get_html(url) 116 root = etree.HTML(html) 117 id = root.xpath('//div[@class="u-cover u-cover-5"]/a[1]/@href') 118 return id[0].strip('/artist?id==') 119 120 121 def search_input_playlist(url): 122 """获取歌单名字和id""" 123 html = selenium_get_html(url) 124 root = etree.HTML(html) 125 id = root.xpath('//div[@class="u-cover u-cover-3"]/a/@href') 126 name = root.xpath('//div[@class="u-cover u-cover-3"]//span/@title') 127 id = [i.strip('/playlist?id==') for i in id] 128 return zip(name, id) 129 130 131 def main(name, choose_id): 132 if choose_id == 1: 133 url = 'https://music.163.com/#/search/m/?s={}&type=1'.format(name) 134 com = search_input_song(url) 135 ids = [] 136 for i, j, k in com: 137 ids.append(k) 138 print("歌曲名称:{0}-------演唱者:{1}-------id:{2}".format(i, j, k)) 139 while True: 140 id = input("请输入需要下载的id(输入q退出):") 141 if id == 'q': 142 return 143 if id in ids: 144 song_id_down(id) 145 return 146 print("请输入正确的id!!!") 147 148 149 elif choose_id == 2: 150 url = 'https://music.163.com/#/search/m/?s={}&type=100'.format(name) 151 id = search_input_artist(url) 152 artist_id_down(id) 153 elif choose_id == 3: 154 url = 'https://music.163.com/#/search/m/?s={}&type=1000'.format(name) 155 com = search_input_playlist(url) 156 ids = [] 157 for i, j in com: 158 ids.append(j) 159 print("歌单名称:{0}-------id:{1}".format(i, j)) 160 while True: 161 id = input("请输入需要下载的id(输入q退出):") 162 if id == 'q': 163 return 164 if id in ids: 165 playlist_id_down(id) 166 return 167 print("请输入正确的id(输入q退出):") 168 169 170 def recognition(): 171 """判断系统,执行清屏命令""" 172 sysstr = platform.system() 173 if (sysstr == "Windows"): 174 os.system('cls') 175 elif (sysstr == "Linux"): 176 os.system('clear') 177 if __name__ == '__main__': 178 path = input("请输入完整路径地址:") 179 if not os.path.exists(path): 180 os.makedirs(path) 181 while True: 182 print("=========================") 183 print("请按提示选择搜索类型:") 184 print("1.歌曲") 185 print("2.歌手") 186 print("3.歌单") 187 print("4.退出") 188 print("=========================") 189 choose_id = int(input("搜索类型:")) 190 191 192 if choose_id == 4: 193 break 194 elif choose_id != 1 and choose_id != 2 and choose_id != 3: 195 print("请按要求输入!!!") 196 continue 197 else: 198 recognition() 199 name = input("请输入搜索内容:") 200 main(name, choose_id) 201 print("3秒后返回主页面") 202 time.sleep(3)
6、结语
这篇文章和代码是网易云音乐的下载,里面的数据比较全面,大块可分为:评论用户信息、评论信息、用户权限信息、评论回复信息等。本文爬取的数据有,评论人昵称、评论时间、评论内容、用户ID。这篇文章就到这里了,如果有不足的地方欢迎各位大佬指定一下,谢谢!
