你是否是經常想要在各大音樂網站上下載音樂?可是網站卻逼迫你下載他們的應用?然而,你下載了應用,它們卻逼迫你購買vip......不要緊,今天咱們就來用爬蟲手段「制裁」這些網站!首先,就由最簡單的酷狗音樂開始爬!php
讓用戶輸入要搜索的音樂名,而後把全部的音樂以及每個音樂對應的信息展現給用戶。再詢問用戶要不要下載任何音樂,若是要,則讓用戶輸入音樂對應的id號來下載(支持批量下載)。python
首先,在獲取多首歌曲的信息和下載地址以前,咱們須要知道如何獲取一首歌的下載地址。正則表達式
打開www.kugou.com ,在搜索欄裏輸入你想要查找的歌曲名。按下回車。切換網頁以後,點進一首歌曲的播放頁。按下F12,調出開發者工具。選擇network,而後點all。能夠看到,目前是沒有任何東西顯示的。由於全部的文件已經在你打開開發者工具的時候加載完了,此時此刻,你只須要F5刷新一下網頁。好了,如今
你就能看到相似這樣的頁面。
能夠看到什麼js文件啊,png文件啊,音頻文件啊,都沒有!由於咱們在調出開發者工具以前,網站已經加載完了文件,這個時候,咱們只須要按下F5刷新一下網站。好了,全部的文件加載出來了。進入到一個叫作index.php?的文件,而後進入到這個文件的地址。api
、工具
進入這個文件地址以後,這實際上就是音樂的信息(爲了方便,我在文章後面就說是信息地址)。咱們還能夠看到一個叫play_url的東西,這個play_url就是音頻的mp3文件地址,能夠看到,這些play_url都是把/變成了/。咱們不用擔憂這個,由於網址輸入欄會自動幫咱們調整成/,可是在用代碼實現爬蟲的時候,咱們就須要把/變成/了。但短期內,咱們先不用管這個。讓咱們進入到這個網址,咦?這不是咱們剛剛播放的音樂嗎?網站
成功以後,咱們就有了更大的信心和思路去爬蟲。咱們只要把每首歌曲的信息地址找出來,而後用正則表達式把每首歌曲的信息和音樂地址獲取出來。再一次用爬蟲獲取到音樂的二進制編碼,保存在本地。編碼
那咱們如何獲取每首歌的信息地址呢?經過拼接地址!讓咱們看這兩首歌的url有啥不一樣,你就知道了。url
Faded - https://wwwapi.kugou.com/yy/i...spa
卡路里 - https://wwwapi.kugou.com/yy/i...3d
能夠看到除了hash值之外的東西,就沒有啥區別了。也就是說咱們只須要經過https://wwwapi.kugou.com/yy/i...
來拼接每首歌的信息地址就好了。那歌曲的hash要去那裏找呢?回到酷狗的音樂搜索欄,隨便搜一首歌按下回車。能夠看到這裏有好多首歌。F12-NETWORK-ALL-F5,咱們找出一個這樣的文件。
咱們進入這個網址,就能夠看到剛剛全部歌曲的hash。那問題又來了,咱們又要怎樣獲取到這個hash信息網址呢?這個太簡單了,只須要經過https://songsearch.kugou.com/...
拼接網址就行。
這個搜索的歌曲名,咱們代碼用input讓用戶輸入歌曲名就好了。那麼,你找到思路了嗎?
思路:拼接出hash信息網址,正則表達式獲取到全部歌曲的hash,再拼接出單首歌曲的url。最後再一次用正則表達式獲取歌曲的play_url便可。
首先導入咱們的requests和re正則表達式庫。re用來找出音樂的信息和下載地址,requests負責獲取文本和下載音樂。
import requests import re
咱們還要設置一些變量,這些變量在後面但是會派上大用場的。
timer = 0 song_urls = {} names = {}
咱們不是要拼接出多首歌曲的信息網址嗎?那咱們就先要讓用戶輸入歌曲名。接着再拼。
songs = input("請輸入歌曲名:") url = 'https://songsearch.kugou.com/song_search_v2?callback=jQuery112409090559630919017_1585358668138&keyword=%s&page=1&pagesize=30&userid=-1&clientver=&platform=WebFilter&tag=em&filter=2&iscorrection=1&privilege_filter=0&_=1585358668140'%songs
如今,咱們就能夠用requests請求文本了!因爲這個網址是get請求的並且咱們請求的是文本,因此,咱們也要用方法requests.get().text方法。
texts = requests.get(url).text
接着,你能夠試着打印一下文本。打印出來的文本和咱們拼接的網址的內容毫無區別(我這裏就不打印了,等下python卡死就完了)
在這些文本里,咱們能夠獲取到每首歌的hash值。用正則表達式查找就好了。
song_hashes = re.findall('"FileHash":"(.*?)"',texts)
打印一下song_hashes,能夠看到,他是個列表。因此咱們要進行for遍歷。
for i in song_hashes: information_url = 'https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery19104610954889760035_1585364074033&hash=%s&album_id=0&dfid=2SSGs60RKO9P0bAzIe0xF4Us&mid=5a959954d2f99fc1438fe2efb7596511&platid=4&_=1585364074034'%i information = requests.get(information_url).text song_url = re.findall('"play_url":"(.*?)"',information) song_names = bytes(re.findall('"audio_name":"(.*?)"',information)[0],encoding='ascii').decode('unicode-escape') singers = bytes(re.findall('"author_name":"(.*?)"',information)[0],encoding='ascii').decode('unicode-escape') if song_names not in names.values(): names[str(timer)] = song_names print("%d.%s"%(timer,song_names)) print("做者:%s"%singers) print() timer += 1 if song_url[0] not in song_urls.values(): song_urls[str(timer-1)] = song_url[0]
上段代碼中,咱們進行了每一個hash的拼接操做,而後咱們在從單首歌曲的信息文本里找到了音樂名和做者和下載地址。因爲音樂名和做者是進行ascii編碼過的,因此咱們也要進行一個解碼。因爲歌曲名和歌手有時候會重複打印,因此咱們每一次打印音樂和做者以前,都會把音樂和做者名加入到一個字典。每一次打印都會進行一次是否存在字典的判斷。字典的key就由咱們的timer變量的變化進行改變key名。另外,咱們還把每首歌的下載地址保存到了song_urls字典裏。
打印了音樂信息以後,就要詢問用戶要下載那首歌了。
print('輸入n就不下載,若要下載多首歌曲,請用英文符號","隔開') choice = input('請輸入要下載歌曲的編號:').split(',') if choice == "n": exit() else: path = input("請輸入要保存的路徑:") for i in choice: song_url = song_urls[i].replace('\\/','/') song = requests.get(song_url).content save_name = names[i] with open(path + '/' + save_name + '.mp3','wb') as f: f.write(song) print("保存完成!")
按之前的作法,用requests.get().content把音樂轉換成二進制文件再進行保存。在get以前,咱們還須要把網址的亂七八糟的\/變成/。以後,就能保存下來了!
咱們就拿一首叫作the day you went away的歌試一下
代碼實現效果:
酷狗每隔一段時間都會弄個滑動驗證碼,這個時候咱們的程序就不能獲取到數據。這種狀況,用selenium就能夠輕鬆解決。
完整代碼:
#導入庫 import requests import re import os #設置好一些變量 timer = 0 #設置一個計算歌曲順序的機器 song_urls = {} names = {} songs = input("請輸入歌曲名:") url = 'https://songsearch.kugou.com/song_search_v2?callback=jQuery112409090559630919017_1585358668138&keyword=%s&page=1&pagesize=30&userid=-1&clientver=&platform=WebFilter&tag=em&filter=2&iscorrection=1&privilege_filter=0&_=1585358668140'%songs texts = requests.get(url).text song_hashes = re.findall('"FileHash":"(.*?)"',texts) print("請稍等...") for i in song_hashes: information_url = 'https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery19104610954889760035_1585364074033&hash=%s&album_id=0&dfid=2SSGs60RKO9P0bAzIe0xF4Us&mid=5a959954d2f99fc1438fe2efb7596511&platid=4&_=1585364074034'%i information = requests.get(information_url).text song_url = re.findall('"play_url":"(.*?)"',information) song_names = bytes(re.findall('"audio_name":"(.*?)"',information)[0],encoding='ascii').decode('unicode-escape') singers = bytes(re.findall('"author_name":"(.*?)"',information)[0],encoding='ascii').decode('unicode-escape') if song_names not in names.values(): names[str(timer)] = song_names print("%d.%s"%(timer,song_names)) print("做者:%s"%singers) print() timer += 1 if song_url[0] not in song_urls.values(): song_urls[str(timer-1)] = song_url[0] print('輸入n就不下載,若要下載多首歌曲,請用英文符號","隔開') choice = input('請輸入要下載歌曲的編號:').split(',') if choice == "n": exit() else: path = input("請輸入要保存的路徑:") has_path = os.path.exists(path) while has_path == False: print("路徑不存在!!") path = input("請輸入要保存的路徑:") has_path = os.path.exists(path) for i in choice: song_url = song_urls[i].replace('\\/','/') song = requests.get(song_url).content save_name = names[i] with open(path + '/' + save_name + '.mp3','wb') as f: f.write(song) print("保存完成!")