1. 程式人生 > 其它 >python | 爬取網易雲音樂下載

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。這篇文章就到這裡了,如果有不足的地方歡迎各位大佬指定一下,謝謝!