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