文 | 某某白米飯python
來源:Python 技術「ID: pythonall」json
如今的音樂APP有不少,爲了避免下載不少的APP,因此咱用python作了一個聚合的音樂下載器,如今聚合了咪咕音樂、QQ音樂,下面是效果圖app
須要安裝一個輔助模塊 prettytable,用於美化控制檯的表格輸出dom
pip install prettytable
如下載 QQ 音樂爲例,在首頁(https://y.qq.com/) 上的搜索框中搜索 <<厚顏無恥>>, 打開 F12 的控制檯面板,能夠找到以下圖的搜索連接,這個連接返回的是一個音樂列表的 json 串ide
def get_request(self, url): try: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36' } response = requests.get(url, headers = headers) if response.status_code == 200: return response except Exception as e: print("請求出錯:", e) return None def search_music(self, key): # 20: 查詢 20 條數據,key:關鍵字 url = 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p=1&n=%d&w=%s' % (20, key) resp = self.get_request(url) resp_json = json.loads(resp.text[9:][:-1]) data_song_list = resp_json['data']['song']['list'] song_list = [] for song in data_song_list: singers = [s.get("name", "") for s in song.get("singer", "")] song_list.append({'name': song['songname'], 'songmid': song['songmid'], 'singer': '|'.join(singers)}) return song_list
示例結果:網站
[{'name': '富士山下', 'songmid': '003dtkNk26WhJD', 'singer': '陳奕迅'}, {'name': '不要說話', 'songmid': '002B2EAA3brD5b', 'singer': '陳奕迅'}, ...., {'name': '最佳損友', 'songmid': '003hFxQh276Cv5', 'singer': '陳奕迅'}]
把音樂列表頁中的歌曲點擊到播放音樂的頁面,在控制面板找到多個以 m4a 結尾的音樂實際連接ui
它的參數部分有一個 vkey 的參數,把 vkey 看成關鍵字在 Network 面板中搜索,找到一個 musics.fcg 結尾的連接,vkey 的數據就在它返回的 json 串中,另外的 purl 的值就是上面的 m4a 連接,最後將 https://ws.stream.qqmusic.qq.com 和 purl 拼湊成音樂連接,musics.fcg 連接中 guid 參數是一個隨機數,songmid 參數是上面單個音樂的 songmid,uin 參數是 QQ 號url
def download_url(self, song): guid = str(random.randrange(1000000000, 10000000000)) purl_url = 'https://u.y.qq.com/cgi-bin/musicu.fcg?' \ '&data={"req":{"param":{"guid":" %s"}},' \ '"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"%s","songmid":["%s"],"uin":"%s"}},"comm":{"uin":%s}}' \ % (guid, guid, song['songmid'], 0, 0) resp = self.get_request(purl_url) if resp is None: return 'N', 'None', '.m4a' resp_json = json.loads(resp.text) purl = resp_json['req_0']['data']['midurlinfo'][0]['purl'] # 有些音樂在網站上不能聽 if len(purl) < 1: msg = 'N' download_url = 'http://ws.stream.qqmusic.qq.com/' + purl song_data = self.get_request(download_url) if song_data: msg = 'Y' return msg, download_url, '.m4a'
示例結果:命令行
只有一個域名的地址的下載連接表示這個音樂只能在客戶端聽,網頁版聽不了3d
到這裏已經完了 QQ 音樂的搜索、抓取腳本,用一樣的方式抓取咪咕音樂(http://m.music.migu.cn)作成咪咕音樂腳本,咪咕音樂更容易爬取
主界面的主要功能就是以表格的方式顯示搜索到的音樂和以序號的方式下載音樂
import os from qqMusic import QQMusic from miguMusic import MiGuMusic from prettytable import PrettyTable class MusicBox(object): def __init__(self): pass def download(self, data, songName, type): save_path = 'music/' + songName + '.' + type file = 'music' if os.path.exists(file): pass else: os.mkdir('music') try: print("{}下載中.....".format(songName), end='') with open(save_path, 'wb') as f: f.write(data) print("已下載完成") except Exception as err: print("文件寫入出錯:", err) return None def main(self): print('請輸入須要下載的歌曲或者歌手:') key = input() print('正在查詢..\033[32mQQ音樂\033[0m', end='') qqMusic = QQMusic() qq_song_list = qqMusic.main(key) print('...\033[31m咪咕音樂\033[0m') miguMusic = MiGuMusic() migu_song_list = miguMusic.main(key) qq_song_list.extend(migu_song_list) song_dict = {} for song in qq_song_list: key = song['name'] + '\\' + song['singer'] s = song_dict.get(key) if s: if s['msg'] != 'Y': song_dict[key] = song else: song_dict[key] = song i = 0 table = PrettyTable(['序號', '歌手', '下載', '歌名']) table.border = 0 table.align = 'l' for song in list(song_dict.values()): i = i + 1 table.add_row([str(i), song['singer'], song['msg'], song['name']]) print(table) while 1: print('\n請輸入須要下載,按 q 退出:') index = input() if index == 'q': return song = list(song_dict.values())[int(index) - 1] data = qqMusic.get_request(song['downloadUrl']) if song['msg'] == 'Y': self.download(data.content, song['name'], song['type']) else: print('該歌曲不容許下載') if __name__ == '__main__': musicBox = MusicBox() musicBox.main()
音樂聚合下載器最重要的部分仍是爬蟲抓取各個音樂網站的數據,命令行的方式則省去了畫 GUI 的工做。