全部代碼
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()) # 綁定Enter鍵
# 歌單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()