网易云音乐下载器
全部代码
- import os
- import json
- import requests
- from tkinter import *
-
-
- class couldmusic(object):
-
- def __init__(self, tk_root):
- """初始化"""
-
- self.root = tk_root
-
- self.playlist_id = StringVar() # 歌单ID,可以通过网易云歌单详情页面获地址栏取
- self.song_id = StringVar() # 歌曲ID,可以通过歌曲播放页面地址栏获取
- self.song_name = StringVar() # 歌曲名字,用于显示下载信息
- self.song_name.set("")
- self.var = StringVar() # 下载进度值
- self.var.set("开始")
-
- self.index = 0
-
- self.song_list = {}
- self.save_path = os.path.abspath(".") # 获取工作目录
- self.save_path = os.path.join(self.save_path, "网易云音乐") # 生成网易云音乐文件夹路径
-
- # 浏览器Headers信息
- self.headers = {
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36",
- }
-
- # 绘制UI
- self.layout()
-
- def process(self):
- """下载进度条"""
-
- total_length = 260 # 进度条总长度
- if len(self.song_list) > 0: # 判断歌曲列表非空
- length = total_length * self.index / len(self.song_list) # 计算下载进度长度
- self.canvas.coords(self.processbar, (0, 0, length,26)) # 创建进度条(起始X坐标,起始Y坐标, 长度,高度)
- self.var.set(f"{self.index}/{len(self.song_list)}") # 设定进度值
- if length == total_length: # 判断是否下载完成
- self.song_name.set("下载完成")
- self.root.update()
-
- def reset(self):
- """重置"""
-
- self.song_id.set("")
- self.playlist_id.set("")
- self.song_list.clear()
- self.var.set("开始")
- self.index = 0
- self.save_path = os.path.abspath(".")
- self.save_path = os.path.join(self.save_path, "网易云音乐")
-
- def get_playlist(self, playlist_id):
-
- playlist_api = "http://music.163.com/api/playlist/detail?id=" # 歌单详情API
- playlist_url = playlist_api + playlist_id # 获取歌单详情地址
-
- r = requests.get(playlist_url, headers = self.headers) # 获取源码
- json_data = json.loads(r.text) # 源码转换Json格式
-
- # 尝试提取指定信息
- try:
- song_list_name = json_data['result']['creator']['nickname'] # 提取歌单名字
- self.song_name.set(f"{song_list_name} 的歌单")
- self.save_path = os.path.join(self.save_path, song_list_name) # 生成歌单文件路径
- if not os.path.isdir(self.save_path): # 判断路径是否存在
- os.mkdir(self.save_path) # 创建歌单文件夹
- except:
- self.song_name.set("请输入有效的歌单ID或歌曲ID")
- return
-
- # 提取歌曲信息
- for i in json_data['result']['tracks']:
- song_name = i["name"].split("/")[0] # 获取歌曲名字,并移除 "/"
- song_id = i['id'] # 获取歌曲ID信息
- self.song_list[song_name] = song_id # 将歌曲信息加入到字典备用
-
- return song_list_name
-
- def get_song_info(self, song_id):
- """获取单首歌曲信息"""
-
- song_detail_api = "http://music.163.com/api/song/detail/?id=song_id&ids=%5Bsong_id%5D" # 歌曲信息API
- song_url = song_detail_api.replace("song_id", self.song_id.get()) # 生成歌曲信息Url
-
- r = requests.get(song_url, headers = self.headers) # 获取源码
- json_data = json.loads(r.text) # 源码转换Json格式
-
- # 尝试提取指定信息
- try:
- for i in json_data['songs']: # 获取Json_data中 ‘songs’内容
- self.song_list[i['name']] = self.song_id.get() # 将歌曲信息加入到字典备用
- except:
- self.song_name.set("请输入有效的歌单ID或歌曲ID_2")
-
- def download(self):
- """下载 "self.song_list" 中歌曲"""
-
- api = "http://music.163.com/song/media/outer/url?id=song_id.mp3"
- # api = "https://www.apicp.cn/API/wyy/api.php?id=" # 网易云解析API, 第三方提供
- self.index = 0
-
- for item in self.song_list: # 遍历歌曲列表
- self.index += 1 # 歌曲次序
- song_url = api.replace("song_id", str(self.song_list[item])) # 解析地址
- name = item + ".mp3" # 歌曲名字
- split_list = ["<", ">", "/", "\\"] # Windows名字机制,移除特殊字符
- for x in split_list:
- name = name.split(x)[0]
- path = os.path.join(self.save_path, name) # 歌曲保存路径
- self.song_name.set(name) # 更新UI信息
- content = requests.get(song_url, headers = self.headers).content # 获取源码
- if content[0] != 0x49: # 判断返回值是否属于音频文件
- self.song_name.set("灰色歌曲无法下载") # 灰色歌曲无法下载
- continue # 跳出本次循环
- if not os.path.isfile(path): # 判断文件是否存在
- with open(path, "wb") as f: # 保存文件
- f.write(content)
- self.process()
-
- def run(self):
- """获取输入信息, 并执行程序"""
-
- playlist_id = self.playlist_id.get() # 获取页面输入歌单ID信息
- song_id = self.song_id.get() # 获取页面输入歌曲ID信息
-
- if not os.path.isdir(self.save_path): # 判断路径是否存在
- os.mkdir(self.save_path) # 创建文件夹
-
- # 判断输入歌单ID和歌曲ID有效
- if playlist_id == "" and song_id == "": # 判断歌单/歌曲非空
- self.song_name.set("请输入有效的歌单ID或歌曲ID_1")
- return
- elif playlist_id: # 歌单ID非空,执行歌单处理函数
- self.get_playlist(playlist_id)
- elif song_id: # 歌曲ID非空,执行歌曲处理函数
- self.get_song_info(song_id)
-
- self.download() # 执行歌曲下载函数
- self.reset() # 重置
-
- def layout(self):
- """绘制界面"""
-
- self.root.title("网易云下载器") # 标题
- self.root.geometry("260x100+600+300") # 位置
- self.root.bind("<Return>", lambda x:self.run()) # 绑定回车键
-
- # 歌单ID
- Label(self.root, text = "歌单ID:").grid(row = 0, column = 0)
- Entry(self.root, textvariable = self.playlist_id).grid(row = 0, column =1, sticky=N+E+S+W)
-
- # 歌曲ID
- Label(self.root, text = "歌曲ID:").grid(row = 1, column =0)
- Entry(self.root, textvariable = self.song_id).grid(row = 1,column = 1, sticky=N+E+S+W)
-
- # 正在下载的歌曲信息
- Label(self.root, text = "正在下载:").grid(row = 2, column =0)
- Entry(self.root, textvariable = self.song_name).grid(row = 2,column = 1, sticky=N+E+S+W)
-
- # 进度条
- self.canvas = Canvas(self.root,height=26, bg="white")
- self.canvas.grid(row = 3, column = 1)
- self.processbar = self.canvas.create_rectangle(0,0,0,0,width = 0,fill = "green")
-
- # 开始
- Button(self.root, textvariable = self.var, command = self.run).grid(row = 3, column = 0, sticky=N+E+S+W)
-
-
- if __name__ == "__main__":
- root = Tk()
- couldmusic(root)
- root.mainloop()
-