python | 爬取網易雲音樂下載
一、選題背景
由於現在的音樂版權問題,很多音樂分佈在各個平臺的音樂播放器,而版權問題也使很多人非常的困擾,從而找不到音樂的資源。因此為幫助使用網易雲的夥伴們,更好的找到各個平臺的資源,聽到更多自己喜歡的歌。
二、爬蟲方案設計
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。這篇文章就到這裡了,如果有不足的地方歡迎各位大佬指定一下,謝謝!