本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。
以下文章来源于Python技术,作者 派森酱
转载地址
<code><span class="hljs-attribute">https:<span class="hljs-comment">//www.gaodaima.<a href="https://www.gaodaima.com/tag/com" title="查看更多关于com的文章" target="_blank">com</a>/fei347795790?t=1</span></span></code>
www#gaodaima.com来源gaodai$ma#com搞$代*码网搞代码
现在的音乐APP有很多,为了不下载很多的APP,所以咱用python做了一个聚合的音乐下载器,现在聚合了咪咕音乐、QQ音乐,下面是效果图
安装
需要安装一个辅助模块 prettytable,用于美化控制台的表格输出
<code><span class="hljs-attribute">pip install prettytable</span></code>
提取音乐链接
搜索音乐
以下载 QQ 音乐为例,在首页(https://y.qq.com/) 上的搜索框中搜索 <<厚颜无耻>>, 打开 F12 的控制台面板,可以找到如下图的搜索链接,这个链接返回的是一个音乐列表的 json 串
<code><span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">get_request<span class="hljs-params">(self, url): <span class="hljs-keyword">try: headers = { <span class="hljs-string">"User-Agent": <span class="hljs-string">"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) <span class="hljs-keyword">if response.status_code == <span class="hljs-number">200: <span class="hljs-keyword">return response <span class="hljs-keyword">except Exception <span class="hljs-keyword">as e: print(<span class="hljs-string">"请求出错:", e) <span class="hljs-keyword">return <span class="hljs-literal">None <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">search_music<span class="hljs-params">(self, key): <span class="hljs-comment"># 20: 查询 20 条数据,key:关键字 url = <span class="hljs-string">"https://c.y.qq.com/soso/fcgi-bin/client_search_cp?p=1&n=%d&w=%s" % (<span class="hljs-number">20, key) resp = self.get_request(url) resp_json = json.loads(resp.text[<span class="hljs-number">9:][:<span class="hljs-number">-1]) data_song_list = resp_json[<span class="hljs-string">"data"][<span class="hljs-string">"song"][<span class="hljs-string">"list"] song_list = [] <span class="hljs-keyword">for song <span class="hljs-keyword">in data_song_list: singers = [s.get(<span class="hljs-string">"name", <span class="hljs-string">"") <span class="hljs-keyword">for s <span class="hljs-keyword">in song.get(<span class="hljs-string">"singer", <span class="hljs-string">"")] song_list.append({<span class="hljs-string">"name": song[<span class="hljs-string">"songname"], <span class="hljs-string">"songmid": song[<span class="hljs-string">"songmid"], <span class="hljs-string">"singer": <span class="hljs-string">"|".join(singers)}) <span class="hljs-keyword">return song_list </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
示例结果:
<code><span class="hljs-selector-attr">[{<span class="hljs-string">"name": <span class="hljs-string">"富士山下", <span class="hljs-string">"songmid": <span class="hljs-string">"003dtkNk26WhJD", <span class="hljs-string">"singer": <span class="hljs-string">"陈奕迅"}, {<span class="hljs-string">"name": <span class="hljs-string">"不要说话", <span class="hljs-string">"songmid": <span class="hljs-string">"002B2EAA3brD5b", <span class="hljs-string">"singer": <span class="hljs-string">"陈奕迅"}, ...., {<span class="hljs-string">"name": <span class="hljs-string">"最佳损友", <span class="hljs-string">"songmid": <span class="hljs-string">"003hFxQh276Cv5", <span class="hljs-string">"singer": <span class="hljs-string">"陈奕迅"}]</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
获取下载链接
把音乐列表页中的歌曲点击到播放音乐的页面,在控制面板找到多个以 m4a 结尾的音乐实际链接
它的参数部分有一个 vkey 的参数,把 vkey 当作关键字在 Network 面板中搜索,找到一个 musics.fcg 结尾的链接,vkey 的数据就在它返回的 json 串中,另外的 purl 的值就是上面的 m4a 链接,最后将 https://ws.stream.qqmusic.qq.com 和 purl 拼凑成音乐链接,musics.fcg 链接中 guid 参数是一个随机数,songmid 参数是上面单个音乐的 songmid,uin 参数是 QQ 号
<code>def download_url(<span class="hljs-keyword">self, song): guid = <span class="hljs-built_in">str(random.randrange(<span class="hljs-number">1000000000, <span class="hljs-number">10000000000)) purl_url = <span class="hljs-symbol">"https:<span class="hljs-comment">//u.y.qq.com/cgi-bin/musicu.fcg?" "&data={<span class="hljs-string">"req":{<span class="hljs-string">"param":{<span class="hljs-string">"guid":<span class="hljs-string">" %s"}}," "<span class="hljs-string">"req_0":{<span class="hljs-string">"module":<span class="hljs-string">"vkey.GetVkeyServer",<span class="hljs-string">"method":<span class="hljs-string">"CgiGetVkey",<span class="hljs-string">"param":{<span class="hljs-string">"guid":<span class="hljs-string">"%s",<span class="hljs-string">"songmid":[<span class="hljs-string">"%s"],<span class="hljs-string">"uin":<span class="hljs-string">"%s"}},<span class="hljs-string">"comm":{<span class="hljs-string">"uin":%s}}" % (guid, guid, song[<span class="hljs-symbol">"songmid"], <span class="hljs-number">0, <span class="hljs-number">0) resp = <span class="hljs-keyword">self.get_request(purl_url) <span class="hljs-keyword">if resp is <span class="hljs-literal">None: <span class="hljs-keyword">return <span class="hljs-string">"N", <span class="hljs-symbol">"None", ".m4a" resp_json = json.loads(resp.text) purl = resp_json[<span class="hljs-symbol">"req_0"][<span class="hljs-symbol">"data"][<span class="hljs-symbol">"midurlinfo"][<span class="hljs-number">0][<span class="hljs-symbol">"purl"] # 有些音乐在网站上不能听 <span class="hljs-keyword">if len(purl) < <span class="hljs-number">1: msg = <span class="hljs-string">"N" download_url = <span class="hljs-symbol">"http:<span class="hljs-comment">//ws.stream.qqmusic.qq.com/" + purl song_data = <span class="hljs-keyword">self.get_request(download_url) <span class="hljs-keyword">if song_data: msg = <span class="hljs-string">"Y" <span class="hljs-keyword">return msg, download_url, ".m4a" </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
示例结果:
只有一个域名的地址的下载链接表示这个音乐只能在客户端听,网页版听不了
到这里已经完了 QQ 音乐的搜索、抓取脚本,用同样的方式抓取咪咕音乐(http://m.music.migu.cn)做成咪咕音乐脚本,咪咕音乐更容易爬取
命令行主界面
主界面的主要功能就是以表格的方式显示搜索到的音乐和以序号的方式下载音乐
<code><span class="hljs-keyword">import os <span class="hljs-keyword">from qqMusic <span class="hljs-keyword">import QQMusic <span class="hljs-keyword">from miguMusic <span class="hljs-keyword">import MiGuMusic <span class="hljs-keyword">from prettytable <span class="hljs-keyword">import PrettyTable <span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">MusicBox<span class="hljs-params">(object): <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">__init__<span class="hljs-params">(self): <span class="hljs-keyword">pass <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">download<span class="hljs-params">(self, data, songName, type): save_path = <span class="hljs-string">"music/" + songName + <span class="hljs-string">"." + type file = <span class="hljs-string">"music" <span class="hljs-keyword">if os.path.exists(file): <span class="hljs-keyword">pass <span class="hljs-keyword">else: os.mkdir(<span class="hljs-string">"music") <span class="hljs-keyword">try: print(<span class="hljs-string">"{}下载中.....".format(songName), end=<span class="hljs-string">"") <span class="hljs-keyword">with open(save_path, <span class="hljs-string">"wb") <span class="hljs-keyword">as f: f.write(data) print(<span class="hljs-string">"已下载完成") <span class="hljs-keyword">except Exception <span class="hljs-keyword">as err: print(<span class="hljs-string">"文件写入出错:", err) <span class="hljs-keyword">return <span class="hljs-literal">None <span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">main<span class="hljs-params">(self): print(<span class="hljs-string">"请输入需要下载的歌曲或者歌手:") key = input() print(<span class="hljs-string">"正在查询..33[32mQQ音乐33[0m", end=<span class="hljs-string">"") qqMusic = QQMusic() qq_song_list = qqMusic.main(key) print(<span class="hljs-string">"...33[31m咪咕音乐33[0m") miguMusic = MiGuMusic() migu_song_list = miguMusic.main(key) qq_song_list.extend(migu_song_list) song_dict = {} <span class="hljs-keyword">for song <span class="hljs-keyword">in qq_song_list: key = song[<span class="hljs-string">"name"] + <span class="hljs-string">"" + song[<span class="hljs-string">"singer"] s = song_dict.get(key) <span class="hljs-keyword">if s: <span class="hljs-keyword">if s[<span class="hljs-string">"msg"] != <span class="hljs-string">"Y": song_dict[key] = song <span class="hljs-keyword">else: song_dict[key] = song i = <span class="hljs-number">0 table = PrettyTable([<span class="hljs-string">"序号", <span class="hljs-string">"歌手", <span class="hljs-string">"下载", <span class="hljs-string">"歌名"]) table.border = <span class="hljs-number">0 table.align = <span class="hljs-string">"l" <span class="hljs-keyword">for song <span class="hljs-keyword">in list(song_dict.values()): i = i + <span class="hljs-number">1 table.add_row([str(i), song[<span class="hljs-string">"singer"], song[<span class="hljs-string">"msg"], song[<span class="hljs-string">"name"]]) print(table) <span class="hljs-keyword">while <span class="hljs-number">1: print(<span class="hljs-string">" 请输入需要下载,按 q 退出:") index = input() <span class="hljs-keyword">if index == <span class="hljs-string">"q": <span class="hljs-keyword">return song = list(song_dict.values())[int(index) - <span class="hljs-number">1] data = qqMusic.get_request(song[<span class="hljs-string">"downloadUrl"]) <span class="hljs-keyword">if song[<span class="hljs-string">"msg"] == <span class="hljs-string">"Y": self.download(data.content, song[<span class="hljs-string">"name"], song[<span class="hljs-string">"type"]) <span class="hljs-keyword">else: print(<span class="hljs-string">"该歌曲不允许下载") <span class="hljs-keyword">if __name__ == <span class="hljs-string">"__main__": musicBox = MusicBox() musicBox.main() </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
总结
音乐聚合下载器最重要的部分还是爬虫抓取各个音乐网站的数据,命令行的方式则省去了画 GUI 的工作。