• 售前

  • 售后

热门帖子
入门百科

python基于tkinter制作无损音乐下载工具(附源码)

[复制链接]
123456809 显示全部楼层 发表于 2021-10-26 14:27:14 |阅读模式 打印 上一主题 下一主题
目次


  • 一.准备工作
  • 二.预览

    • 1.搜索
    • 2.下载
    • 3.效果

  • 三.详细计划
  • 四.源代码

    • 4.1 Music_Search-v1.0.py
    • 4.2 Music_Search_Engine.py

  • 五.总结
继承写GUI,本次依然使用Tkinter计划一款图形界面,使用Tkinter做一款音乐下载软件,听起来听平常的,但是我这款软件可以或许下载 无损音乐下载软件,听起来不错吧,Let`s go!

一.准备工作


python Tkinter

二.预览



1.搜索



2.下载



3.效果


无损音乐就这样下载完了。


三.详细计划


这里仅展示我计划的团体思绪。


四.源代码



4.1 Music_Search-v1.0.py
  1. from tkinter import *
  2. from tkinter import ttk
  3. from tkinter import messagebox
  4. from Music_Search_Engine import Spider
  5. import threading
  6. from tkinter.filedialog import askdirectory
  7. import os
  8. '''
  9. 1.加入e1绑定事件,b1='disable'
  10. 2. 03.15-使用self.flag判断当前下载任务是否完成
  11. 3.实现UI和爬虫分离,返回实时进度
  12. '''
  13. class App:
  14. def __init__(self):
  15. self.w=Tk()
  16. self.w.title('Music_Search-v1.0')
  17. self.w.resizable(0,0)
  18. self.flag=True
  19. width=400
  20. height=560
  21. left=(self.w.winfo_screenwidth()-width)/2
  22. top=(self.w.winfo_screenheight()-height)/2
  23. self.w.geometry('%dx%d+%d+%d'%(width,height,left,top))
  24. self.create_widget()
  25. self.set_widget()
  26. self.place_widget()
  27. self.w.mainloop()
  28. def create_widget(self):
  29. self.e2_var=StringVar()
  30. self.r_choice=IntVar()
  31. self.l3_var=StringVar()
  32. self.l1=ttk.Label(self.w,text='关键字:')
  33. self.e1=ttk.Entry(self.w)
  34. self.b1=ttk.Button(self.w,text='搜索')
  35. self.l4 = ttk.Label(self.w, text='存储路径:')
  36. self.e2 = ttk.Entry(self.w,textvariable=self.e2_var)
  37. self.b2 = ttk.Button(self.w, text='选择')
  38. self.l2=ttk.Label(self.w,text='下载品质:')
  39. self.r1=Radiobutton(self.w,text='标准',value=1)
  40. self.r2=Radiobutton(self.w,text='高品',value=2)
  41. self.r3=Radiobutton(self.w,text='无损',value=3)
  42. self.b3=ttk.Button(self.w,text='下载')
  43. self.listbox=Listbox(self.w)
  44. self.canvas = Canvas(self.w, bg="white")
  45. self.l3=ttk.Label(self.w)
  46. self.m=Menu(self.w)
  47. self.w['menu']=self.m
  48. self.s1=Menu(self.m,tearoff=False)
  49. self.s2=Menu(self.m,tearoff=False)
  50. self.s3=Menu(self.m,tearoff=False)
  51. def set_widget(self):
  52. self.b1.config(command=lambda:self.thread_it(self.search_music))
  53. self.e1.config(justify='center')
  54. self.b2.config(command=self.open_file_savepath)
  55. self.r1.config(variable=self.r_choice,command=self.show_size,state='disable')
  56. self.r2.config(variable=self.r_choice,command=self.show_size,state='disable')
  57. self.r3.config(variable=self.r_choice,command=self.show_size,state='disable')
  58. self.b3.config(command=lambda:self.thread_it(self.pre_download))
  59. self.canvas.config(width=380, height=20)
  60. self.w.bind('<<ListboxSelect>>',self.show_info)
  61. self.e1.bind('<Return>',self.do_search)
  62. self.w.protocol('WM_DELETE_WINDOW',self.quit_window)
  63. self.w.bind('<Escape>',self.do_escape)
  64. self.l3.config(textvariable=self.l3_var,background='lightblue',justify='center')
  65. self.l3_var.set('请先搜索')
  66. self.listbox.config(state='disable')
  67. self.abs_path = os.path.abspath('./')
  68. self.e2_var.set(self.abs_path)
  69. self.e2.config(state='readonly')
  70. self.b3.config(state='disable')
  71. self.m.add_cascade(label='文件',menu=self.s1)
  72. self.s1.add_command(label='打开文件夹',command=self.open_dir)
  73. self.s1.add_separator()
  74. self.s1.add_command(label='退出',command=self.quit_window)
  75. self.m.add_cascade(label='操作',menu=self.s2)
  76. self.s2.add_command(label='搜索',command=lambda:self.thread_it(self.search_music))
  77. self.s2.add_command(label='下载',command=lambda:self.thread_it(self.pre_download))
  78. self.s2.entryconfig("下载",state=DISABLED)
  79. self.m.add_cascade(label='关于',menu=self.s3)
  80. self.s3.add_command(label='说明',command=self.show_explian)
  81. def place_widget(self):
  82. self.l1.place(x=10,y=10)
  83. self.e1.place(x=80,y=10,width=200)
  84. self.b1.place(x=310,y=10,height=25,width=80)
  85. self.l2.place(x=10,y=80)
  86. self.r1.place(x=80,y=80)
  87. self.r2.place(x=160,y=80)
  88. self.r3.place(x=240,y=80)
  89. self.l4.place(x=10,y=50)
  90. self.e2.place(x=80,y=50,width=200)
  91. self.b2.place(x=310,y=45,height=25,width=80)
  92. self.b3.place(x=310,y=80,height=25,width=80)
  93. self.listbox.place(x=10,y=110,width=380,height=380)
  94. self.l3.place(x=0,y=520,width=400,height=35)
  95. self.canvas.place(x=10,y=492)
  96. def thread_it(self,func,*args):
  97. t=threading.Thread(target=func,args=args)
  98. t.setDaemon(True)
  99. t.start()
  100. def do_search(self,event):
  101. self.thread_it(self.search_music)
  102. def search_music(self):
  103. self.l3_var.set('')
  104. self.listbox.delete(0,END)
  105. spider=Spider()
  106. if self.e1.get():
  107. self.music_list=spider.Get_Music_List(self.e1.get())
  108. if self.music_list:
  109. self.listbox.config(state='normal')
  110. counter=1
  111. for data in self.music_list:
  112.   song_name = data.get('song_name')
  113.   self.listbox.insert(END,str(counter)+'、'+song_name)
  114.   self.listbox.update()
  115.   counter+=1
  116. self.l3_var.set(f'共检索到了{len(self.music_list)}首歌曲')
  117. self.s2.entryconfig("下载", state=NORMAL)
  118. self.b3.config(state='normal')
  119. else:
  120. messagebox.showinfo('提示','没有找到相关歌曲,请更换关键字!')
  121. self.l3_var.set('没有找到相关歌曲,请更换关键字!')
  122. self.l3.config(background='lightblue')
  123. else:
  124. messagebox.showerror('错误','请输入关键字!')
  125. self.l3_var.set('请输入关键字!')
  126. self.l3.config(background='red')
  127. def show_info(self, event):
  128. self.r1.config(state='normal')
  129. self.r2.config(state='normal')
  130. self.r3.config(state='normal')
  131. self.r_choice.set(0)
  132. try:
  133. listbox_index = self.listbox.curselection()[0]#获取选中歌曲索引
  134. data=self.music_list[listbox_index]
  135. if data['FileHash']==''and data['FileSize']==0:
  136. self.r1.config(state='disable')
  137. self.r1.config(state='disable')
  138. self.file_size=data['FileSize']
  139. if data['HQFileHash'] == ''and data['HQFileSize']==0:
  140. self.r2.config(state='disable')
  141. self.hq_size=data['HQFileSize']
  142. if data['SQFileHash'] == ''and data['SQFileSize']==0:
  143. self.r3.config(state='disable')
  144. self.sq_size=data['SQFileSize']
  145. self.l3_var.set('歌曲名称:'+data['song_name'])
  146. except (IndexError,TclError):
  147. pass
  148. def show_size(self):
  149. try:
  150. if self.r_choice.get() == 1:
  151. self.l3_var.set('标准格式文件大小:' + self.process_size(self.file_size))
  152. elif self.r_choice.get() == 2:
  153. self.l3_var.set('高品质格式文件大小:' + self.process_size(self.hq_size))
  154. elif self.r_choice.get() == 3:
  155. self.l3_var.set('无损格式文件大小:' + self.process_size(self.sq_size))
  156. except AttributeError:
  157. messagebox.showwarning('警告','请先选择歌曲')
  158. self.r_choice.set(0)
  159. def process_size(self,bytes):
  160. try:
  161. bytes=float(bytes)
  162. kb=bytes/1024
  163. except:
  164. return 'error'
  165. if kb>1024:
  166. mb=kb/1024
  167. if mb>1024:
  168. gb=mb/1024
  169. return '%.2fGB'%gb
  170. else:
  171. return '%.2fMB'%mb
  172. else:
  173. return '%.2fKB'%kb
  174. def open_file_savepath(self):
  175. self.file = askdirectory()
  176. if self.file:
  177. self.e2_var.set(self.file)
  178. def pre_download(self):
  179. listbox_index = self.listbox.curselection()[0] # 获取选中歌曲索引
  180. data = self.music_list[listbox_index]
  181. music_name=data['song_name']
  182. if self.r_choice.get()==1:
  183. FileHash=data['FileHash']
  184. real_link=Spider().get_music_link(FileHash)
  185. type='mp3'
  186. if real_link:
  187. self.download_music(real_link,music_name,type)
  188. else:
  189. messagebox.showwarning('警告','没有此音乐版权,正在争取!')
  190. elif self.r_choice.get()==2:
  191. HQFileHash=data['HQFileHash']
  192. type='mp3'
  193. real_link=Spider().get_music_link(HQFileHash)
  194. if real_link:
  195. self.download_music(real_link,music_name,type)
  196. else:
  197. messagebox.showwarning('警告','没有此音乐版权,正在争取!')
  198. elif self.r_choice.get()==3:
  199. SQFileHash=data['SQFileHash']
  200. type='flac'
  201. real_link=Spider().get_music_link(SQFileHash)
  202. if real_link:
  203. self.download_music(real_link,music_name,type)
  204. else:
  205. messagebox.showwarning('警告','没有此音乐版权,正在争取!')
  206. def download_music(self,music_link,music_name,music_type):
  207. if self.flag:
  208. self.flag=False
  209. file_path=self.e2_var.get()
  210. # 先清空进度条,再下载
  211. self.clean_progressbar()
  212. try:
  213. os.mkdir(file_path+'/My_Music/')
  214. except:
  215. pass
  216. file = file_path+f'/My_Music/{music_name}.{music_type}'
  217. fill_line = self.canvas.create_rectangle(1.5, 1.5, 0, 23, width=0, fill="green")
  218. self.l3_var.set(f'正在下载{music_name}......')
  219. for process in Spider().download_music(music_link,file_path=file):
  220. self.canvas.coords(fill_line, (0, 0, process, 60))
  221. self.w.update()
  222. self.l3_var.set(f'{music_name}.{music_type}下载完成!')
  223. messagebox.showinfo('提示',f'{music_name}.{music_type}下载完成!')
  224. self.flag=True
  225. else:
  226. messagebox.showwarning('警告','请等待当前任务完成!')
  227. def clean_progressbar(self):
  228. # 清空进度条
  229. fill_line = self.canvas.create_rectangle(1.5, 1.5, 0, 23, width=0, fill="white")
  230. x = 500 # 未知变量,可更改
  231. n = 380 / x # 465是矩形填充满的次数
  232. for t in range(x):
  233. n = n + 380 / x
  234. # 以矩形的长度作为变量值更新
  235. self.canvas.coords(fill_line, (0, 0, n, 60))
  236. self.w.update()
  237. def open_dir(self):
  238. file_path=self.e2_var.get()
  239. try:
  240. os.mkdir(file_path + '/My_Music/')
  241. except:
  242. pass
  243. os.startfile(file_path + '/My_Music/')
  244. def show_explian(self):
  245. messagebox.showwarning('敬告','本软件仅供学习交流!')
  246. def do_escape(self,event):
  247. self.quit_window()
  248. def quit_window(self):
  249. ret=messagebox.askyesno('退出','是否要退出?')
  250. if ret:
  251. self.w.destroy()
  252. if __name__ == '__main__':
  253. a=App()
复制代码
4.2 Music_Search_Engine.py
  1. import requests
  2. import re
  3. import json
  4. from urllib import parse
  5. import hashlib
  6. from requests.adapters import HTTPAdapter
  7. class Spider(object):
  8. def clean_txt(self, title): # 清洗标题中不能用于命名文件的字符
  9. rstr = r"[\/\\\:\*\?"\<\>\|]" # '/ \ : * ? " < > |'
  10. title = re.sub(rstr, "_", title) # 替换为下划线
  11. return title
  12. def get_one_page(self, url):
  13. headers = {
  14. 'referer': 'https://www.kugou.com/song/',
  15. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
  16. }
  17. try:
  18. s = requests.Session() # 保持会话
  19. s.mount('http://', HTTPAdapter(max_retries=3)) # 最大重试
  20. s.mount('https://', HTTPAdapter(max_retries=3))
  21. r = s.get(url, headers=headers, timeout=15) # 超时设置
  22. r.raise_for_status() # 状态码 如果不是200则报错
  23. r.encoding = 'utf-8' # r.apparent_encoding#字符类型
  24. return r.text # 返回页面
  25. except:
  26. pass
  27. def Get_Music_List(self, key_word):
  28. result_list=[]
  29. search_url = 'http://songsearch.kugou.com/song_search_v2?keyword={}&page=1'.format(key_word)
  30. total = json.loads(self.get_one_page(search_url))['data']['total']
  31. #total值为0就是没有搜索到相关歌曲
  32. if total != 0:
  33. search_total_url = search_url + '&pagesize=%d' % total
  34. music_list = json.loads(self.get_one_page(search_total_url))['data']['lists'] # 歌曲列表
  35. for music in music_list:
  36. item = {}#防止字典值覆盖
  37. item['song_name']=self.clean_txt(music['FileName'].replace('<em>', '').replace('</em>', '')) # 歌手—歌曲
  38. item['FileHash']=music['FileHash']
  39. item['HQFileHash']=music['HQFileHash']
  40. item['SQFileHash']=music['SQFileHash']
  41. item['FileSize']=music['FileSize']
  42. item['HQFileSize']=music['HQFileSize']
  43. item['SQFileSize']=music['SQFileSize']
  44. result_list.append(item)
  45. return result_list
  46. else:
  47. return None
  48. def v2_md5(self, Hash): # 用于生成key,
  49. return hashlib.md5((Hash + 'kgcloudv2').encode('utf-8')).hexdigest()
  50. def get_music_link(self, hash):
  51. Hash = str.lower(hash) # 小写哈希值
  52. key_new = self.v2_md5(Hash) # 生成v2系统key
  53. Music_api_1 = 'http://trackercdnbj.kugou.com/i/v2/'
  54. params = {
  55. 'cmd': 23,
  56. 'pid': 1,
  57. 'behavior': 'download',
  58. 'hash': Hash,
  59. 'key': key_new
  60. }
  61. try:
  62. real_music_link=json.loads(self.get_one_page(Music_api_1+'?'+parse.urlencode(params)))['url']
  63. return real_music_link
  64. except KeyError:
  65. return None
  66. #实时返回当前下载进度
  67. def download_music(self,music_link,file_path):
  68. headers = {
  69. 'sec-fetch-dest': 'document',
  70. 'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Mobile Safari/537.36'
  71. }
  72. r = requests.get(music_link, headers=headers, stream=True)
  73. chunk_size = 1024 # 每一块的大小,每次下载块的大小
  74. file_size = int(r.headers['Content-Length']) # 提取出来的文件大小为string格式,使用int()强制转化
  75. raise_data = 380 / (file_size / chunk_size) # 增量大小,380为进度条的长度
  76. _size = 0 # 已经下载文件的大小
  77. with open(file_path, "wb") as f:
  78. n = 0
  79. for data in r.iter_content(chunk_size): # inter_content:用于边下载边存硬盘,每次下载chunk_size大小的块
  80.   f.write(data)
  81.   n += raise_data
  82.   yield n
复制代码
五.总结


本次使用TKinter制作一款无损音乐下载软件,工具打包好放在了蓝奏云,请自取。思绪、代码方面有什么不敷欢迎各位大佬指正、品评!如果以为软件还可以,点个赞吧。
以上就是python基于tkinter制作无损音乐下载工具(附源码)的详细内容,更多关于python制作音乐下载工具的资料请关注草根技术分享其它相关文章!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作