python基于tkinter制作无损音乐下载工具

继续写GUI,本次依然使用Tkinter设计一款图形界面,使用Tkinter做一款音乐下载软件,听起来听平常的,但是我这款软件能够下载 无损音乐下载软件,听起来不错吧,Let`s go!

Posted in Python onMarch 29, 2021

一.准备工作

python Tkinter

二.预览

python基于tkinter制作无损音乐下载工具

1.搜索

python基于tkinter制作无损音乐下载工具

2.下载

python基于tkinter制作无损音乐下载工具

3.结果

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

python基于tkinter制作无损音乐下载工具

三.详细设计

这里仅展示我设计的整体思路。

python基于tkinter制作无损音乐下载工具

四.源代码

4.1 Music_Search-v1.0.py

from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from Music_Search_Engine import Spider
import threading
from tkinter.filedialog import askdirectory
import os
 
'''
1.加入e1绑定事件,b1='disable'
2. 03.15-使用self.flag判断当前下载任务是否完成
3.实现UI和爬虫分离,返回实时进度
'''
class App:
 def __init__(self):
 self.w=Tk()
 self.w.title('Music_Search-v1.0')
 self.w.resizable(0,0)
 self.flag=True
 width=400
 height=560
 left=(self.w.winfo_screenwidth()-width)/2
 top=(self.w.winfo_screenheight()-height)/2
 self.w.geometry('%dx%d+%d+%d'%(width,height,left,top))
 self.create_widget()
 self.set_widget()
 self.place_widget()
 self.w.mainloop()
 
 def create_widget(self):
 self.e2_var=StringVar()
 self.r_choice=IntVar()
 self.l3_var=StringVar()
 self.l1=ttk.Label(self.w,text='关键字:')
 self.e1=ttk.Entry(self.w)
 self.b1=ttk.Button(self.w,text='搜索')
 self.l4 = ttk.Label(self.w, text='存储路径:')
 self.e2 = ttk.Entry(self.w,textvariable=self.e2_var)
 self.b2 = ttk.Button(self.w, text='选择')
 self.l2=ttk.Label(self.w,text='下载品质:')
 self.r1=Radiobutton(self.w,text='标准',value=1)
 self.r2=Radiobutton(self.w,text='高品',value=2)
 self.r3=Radiobutton(self.w,text='无损',value=3)
 self.b3=ttk.Button(self.w,text='下载')
 self.listbox=Listbox(self.w)
 self.canvas = Canvas(self.w, bg="white")
 self.l3=ttk.Label(self.w)
 self.m=Menu(self.w)
 self.w['menu']=self.m
 self.s1=Menu(self.m,tearoff=False)
 self.s2=Menu(self.m,tearoff=False)
 self.s3=Menu(self.m,tearoff=False)
 
 def set_widget(self):
 self.b1.config(command=lambda:self.thread_it(self.search_music))
 self.e1.config(justify='center')
 self.b2.config(command=self.open_file_savepath)
 self.r1.config(variable=self.r_choice,command=self.show_size,state='disable')
 self.r2.config(variable=self.r_choice,command=self.show_size,state='disable')
 self.r3.config(variable=self.r_choice,command=self.show_size,state='disable')
 self.b3.config(command=lambda:self.thread_it(self.pre_download))
 self.canvas.config(width=380, height=20)
 self.w.bind('<<ListboxSelect>>',self.show_info)
 self.e1.bind('<Return>',self.do_search)
 self.w.protocol('WM_DELETE_WINDOW',self.quit_window)
 self.w.bind('<Escape>',self.do_escape)
 self.l3.config(textvariable=self.l3_var,background='lightblue',justify='center')
 self.l3_var.set('请先搜索')
 self.listbox.config(state='disable')
 self.abs_path = os.path.abspath('./')
 self.e2_var.set(self.abs_path)
 self.e2.config(state='readonly')
 self.b3.config(state='disable')
 self.m.add_cascade(label='文件',menu=self.s1)
 self.s1.add_command(label='打开文件夹',command=self.open_dir)
 self.s1.add_separator()
 self.s1.add_command(label='退出',command=self.quit_window)
 self.m.add_cascade(label='操作',menu=self.s2)
 self.s2.add_command(label='搜索',command=lambda:self.thread_it(self.search_music))
 self.s2.add_command(label='下载',command=lambda:self.thread_it(self.pre_download))
 self.s2.entryconfig("下载",state=DISABLED)
 self.m.add_cascade(label='关于',menu=self.s3)
 self.s3.add_command(label='说明',command=self.show_explian)
 
 def place_widget(self):
 self.l1.place(x=10,y=10)
 self.e1.place(x=80,y=10,width=200)
 self.b1.place(x=310,y=10,height=25,width=80)
 self.l2.place(x=10,y=80)
 self.r1.place(x=80,y=80)
 self.r2.place(x=160,y=80)
 self.r3.place(x=240,y=80)
 self.l4.place(x=10,y=50)
 self.e2.place(x=80,y=50,width=200)
 self.b2.place(x=310,y=45,height=25,width=80)
 self.b3.place(x=310,y=80,height=25,width=80)
 self.listbox.place(x=10,y=110,width=380,height=380)
 self.l3.place(x=0,y=520,width=400,height=35)
 self.canvas.place(x=10,y=492)
 
 def thread_it(self,func,*args):
 t=threading.Thread(target=func,args=args)
 t.setDaemon(True)
 t.start()
 
 def do_search(self,event):
 self.thread_it(self.search_music)
 
 def search_music(self):
 self.l3_var.set('')
 self.listbox.delete(0,END)
 spider=Spider()
 if self.e1.get():
 self.music_list=spider.Get_Music_List(self.e1.get())
 if self.music_list:
 self.listbox.config(state='normal')
 counter=1
 for data in self.music_list:
  song_name = data.get('song_name')
  self.listbox.insert(END,str(counter)+'、'+song_name)
  self.listbox.update()
  counter+=1
 self.l3_var.set(f'共检索到了{len(self.music_list)}首歌曲')
 self.s2.entryconfig("下载", state=NORMAL)
 self.b3.config(state='normal')
 else:
 messagebox.showinfo('提示','没有找到相关歌曲,请更换关键字!')
 self.l3_var.set('没有找到相关歌曲,请更换关键字!')
 self.l3.config(background='lightblue')
 else:
 messagebox.showerror('错误','请输入关键字!')
 self.l3_var.set('请输入关键字!')
 self.l3.config(background='red')
 
 def show_info(self, event):
 self.r1.config(state='normal')
 self.r2.config(state='normal')
 self.r3.config(state='normal')
 self.r_choice.set(0)
 try:
 listbox_index = self.listbox.curselection()[0]#获取选中歌曲索引
 data=self.music_list[listbox_index]
 if data['FileHash']==''and data['FileSize']==0:
 self.r1.config(state='disable')
 self.r1.config(state='disable')
 self.file_size=data['FileSize']
 if data['HQFileHash'] == ''and data['HQFileSize']==0:
 self.r2.config(state='disable')
 self.hq_size=data['HQFileSize']
 if data['SQFileHash'] == ''and data['SQFileSize']==0:
 self.r3.config(state='disable')
 self.sq_size=data['SQFileSize']
 self.l3_var.set('歌曲名称:'+data['song_name'])
 except (IndexError,TclError):
 pass
 
 def show_size(self):
 try:
 if self.r_choice.get() == 1:
 self.l3_var.set('标准格式文件大小:' + self.process_size(self.file_size))
 elif self.r_choice.get() == 2:
 self.l3_var.set('高品质格式文件大小:' + self.process_size(self.hq_size))
 elif self.r_choice.get() == 3:
 self.l3_var.set('无损格式文件大小:' + self.process_size(self.sq_size))
 except AttributeError:
 messagebox.showwarning('警告','请先选择歌曲')
 self.r_choice.set(0)
 
 def process_size(self,bytes):
 try:
 bytes=float(bytes)
 kb=bytes/1024
 except:
 return 'error'
 if kb>1024:
 mb=kb/1024
 if mb>1024:
 gb=mb/1024
 return '%.2fGB'%gb
 else:
 return '%.2fMB'%mb
 else:
 return '%.2fKB'%kb
 
 def open_file_savepath(self):
 self.file = askdirectory()
 if self.file:
 self.e2_var.set(self.file)
 
 def pre_download(self):
 listbox_index = self.listbox.curselection()[0] # 获取选中歌曲索引
 data = self.music_list[listbox_index]
 music_name=data['song_name']
 if self.r_choice.get()==1:
 FileHash=data['FileHash']
 real_link=Spider().get_music_link(FileHash)
 type='mp3'
 if real_link:
 self.download_music(real_link,music_name,type)
 else:
 messagebox.showwarning('警告','没有此音乐版权,正在争取!')
 elif self.r_choice.get()==2:
 HQFileHash=data['HQFileHash']
 type='mp3'
 real_link=Spider().get_music_link(HQFileHash)
 if real_link:
 self.download_music(real_link,music_name,type)
 else:
 messagebox.showwarning('警告','没有此音乐版权,正在争取!')
 elif self.r_choice.get()==3:
 SQFileHash=data['SQFileHash']
 type='flac'
 real_link=Spider().get_music_link(SQFileHash)
 if real_link:
 self.download_music(real_link,music_name,type)
 else:
 messagebox.showwarning('警告','没有此音乐版权,正在争取!')
 
 def download_music(self,music_link,music_name,music_type):
 if self.flag:
 self.flag=False
 file_path=self.e2_var.get()
 # 先清空进度条,再下载
 self.clean_progressbar()
 
 try:
 os.mkdir(file_path+'/My_Music/')
 except:
 pass
 file = file_path+f'/My_Music/{music_name}.{music_type}'
 fill_line = self.canvas.create_rectangle(1.5, 1.5, 0, 23, width=0, fill="green")
 self.l3_var.set(f'正在下载{music_name}......')
 for process in Spider().download_music(music_link,file_path=file):
 self.canvas.coords(fill_line, (0, 0, process, 60))
 self.w.update()
 self.l3_var.set(f'{music_name}.{music_type}下载完成!')
 messagebox.showinfo('提示',f'{music_name}.{music_type}下载完成!')
 self.flag=True
 else:
 messagebox.showwarning('警告','请等待当前任务完成!')
 
 def clean_progressbar(self):
 # 清空进度条
 fill_line = self.canvas.create_rectangle(1.5, 1.5, 0, 23, width=0, fill="white")
 x = 500 # 未知变量,可更改
 n = 380 / x # 465是矩形填充满的次数
 for t in range(x):
 n = n + 380 / x
 # 以矩形的长度作为变量值更新
 self.canvas.coords(fill_line, (0, 0, n, 60))
 self.w.update()
 
 def open_dir(self):
 file_path=self.e2_var.get()
 try:
 os.mkdir(file_path + '/My_Music/')
 except:
 pass
 os.startfile(file_path + '/My_Music/')
 
 def show_explian(self):
 messagebox.showwarning('敬告','本软件仅供学习交流!')
 
 def do_escape(self,event):
 self.quit_window()
 
 def quit_window(self):
 ret=messagebox.askyesno('退出','是否要退出?')
 if ret:
 self.w.destroy()
 
if __name__ == '__main__':
 a=App()

4.2 Music_Search_Engine.py

import requests
import re
import json
from urllib import parse
import hashlib
from requests.adapters import HTTPAdapter
 
class Spider(object):
 
 def clean_txt(self, title): # 清洗标题中不能用于命名文件的字符
 rstr = r"[\/\\\:\*\?\"\<\>\|]" # '/ \ : * ? " < > |'
 title = re.sub(rstr, "_", title) # 替换为下划线
 return title
 
 def get_one_page(self, url):
 headers = {
 'referer': 'https://www.kugou.com/song/',
 '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'
 }
 try:
 s = requests.Session() # 保持会话
 s.mount('http://', HTTPAdapter(max_retries=3)) # 最大重试
 s.mount('https://', HTTPAdapter(max_retries=3))
 r = s.get(url, headers=headers, timeout=15) # 超时设置
 r.raise_for_status() # 状态码 如果不是200则报错
 r.encoding = 'utf-8' # r.apparent_encoding#字符类型
 return r.text # 返回页面
 except:
 pass
 
 def Get_Music_List(self, key_word):
 result_list=[]
 search_url = 'http://songsearch.kugou.com/song_search_v2?keyword={}&page=1'.format(key_word)
 total = json.loads(self.get_one_page(search_url))['data']['total']
 #total值为0就是没有搜索到相关歌曲
 if total != 0:
 search_total_url = search_url + '&pagesize=%d' % total
 music_list = json.loads(self.get_one_page(search_total_url))['data']['lists'] # 歌曲列表
 for music in music_list:
 item = {}#防止字典值覆盖
 item['song_name']=self.clean_txt(music['FileName'].replace('<em>', '').replace('</em>', '')) # 歌手—歌曲
 item['FileHash']=music['FileHash']
 item['HQFileHash']=music['HQFileHash']
 item['SQFileHash']=music['SQFileHash']
 item['FileSize']=music['FileSize']
 item['HQFileSize']=music['HQFileSize']
 item['SQFileSize']=music['SQFileSize']
 result_list.append(item)
 return result_list
 else:
 return None
 
 def v2_md5(self, Hash): # 用于生成key,
 return hashlib.md5((Hash + 'kgcloudv2').encode('utf-8')).hexdigest()
 
 def get_music_link(self, hash):
 Hash = str.lower(hash) # 小写哈希值
 key_new = self.v2_md5(Hash) # 生成v2系统key
 Music_api_1 = 'http://trackercdnbj.kugou.com/i/v2/'
 params = {
 'cmd': 23,
 'pid': 1,
 'behavior': 'download',
 'hash': Hash,
 'key': key_new
 }
 try:
 real_music_link=json.loads(self.get_one_page(Music_api_1+'?'+parse.urlencode(params)))['url']
 return real_music_link
 except KeyError:
 return None
 
 #实时返回当前下载进度
 def download_music(self,music_link,file_path):
 headers = {
 'sec-fetch-dest': 'document',
 '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'
 }
 r = requests.get(music_link, headers=headers, stream=True)
 chunk_size = 1024 # 每一块的大小,每次下载块的大小
 file_size = int(r.headers['Content-Length']) # 提取出来的文件大小为string格式,使用int()强制转化
 raise_data = 380 / (file_size / chunk_size) # 增量大小,380为进度条的长度
 _size = 0 # 已经下载文件的大小
 with open(file_path, "wb") as f:
 n = 0
 for data in r.iter_content(chunk_size): # inter_content:用于边下载边存硬盘,每次下载chunk_size大小的块
  f.write(data)
  n += raise_data
  yield n

本次使用TKinter制作一款无损音乐下载软件,工具打包好放在了蓝奏云,请自取。

Python 相关文章推荐
python获取豆瓣电影简介代码分享
Jan 16 Python
粗略分析Python中的内存泄漏
Apr 23 Python
Python模拟脉冲星伪信号频率实例代码
Jan 03 Python
Python cookbook(数据结构与算法)找到最大或最小的N个元素实现方法示例
Feb 13 Python
Python cookbook(数据结构与算法)将多个映射合并为单个映射的方法
Apr 19 Python
padas 生成excel 增加sheet表的实例
Dec 11 Python
python抖音表白程序源代码
Apr 07 Python
python实现比对美团接口返回数据和本地mongo数据是否一致示例
Aug 09 Python
Python lambda表达式filter、map、reduce函数用法解析
Sep 11 Python
浅析Django中关于session的使用
Dec 30 Python
python实现删除列表中某个元素的3种方法
Jan 15 Python
TensorFlow学习之分布式的TensorFlow运行环境
Feb 05 Python
Python requests库参数提交的注意事项总结
Python爬虫爬取全球疫情数据并存储到mysql数据库的步骤
Python爬虫数据的分类及json数据使用小结
Mar 29 #Python
python re模块和正则表达式
Mar 24 #Python
opencv实现图像几何变换
PyQt QMainWindow的使用示例
Mar 24 #Python
PyQt 如何创建自定义QWidget
Mar 24 #Python
You might like
c#中的实现php中的preg_replace
2009/12/21 PHP
php入门学习知识点二 PHP简单的分页过程与原理
2011/07/14 PHP
php Ubb代码编辑器函数代码
2012/07/05 PHP
ThinkPHP调试模式与日志记录概述
2014/08/22 PHP
微信开发之网页授权获取用户信息(二)
2016/01/08 PHP
对PHP依赖注入的理解实例分析
2016/10/09 PHP
php5对象复制、clone、浅复制与深复制实例详解
2019/08/14 PHP
JavaScript中Math对象使用说明
2008/01/16 Javascript
前端开发必须知道的JS之原型和继承
2010/07/06 Javascript
为原生js Array增加each方法
2012/04/07 Javascript
js实现的GridView即表头固定表体有滚动条且可滚动
2014/02/19 Javascript
轻松创建nodejs服务器(1):一个简单nodejs服务器例子
2014/12/18 NodeJs
JavaScript学习笔记之基础语法
2015/01/22 Javascript
js获取当前年月日-YYYYmmDD格式的实现代码
2016/06/01 Javascript
jQuery中checkbox反复调用attr('checked', true/false)只有第一次生效的解决方法
2016/11/16 Javascript
3分钟快速搭建nodejs本地服务器方法运行测试html/js
2017/04/01 NodeJs
解决BootStrap Fileinput手机图片上传显示旋转问题
2017/06/01 Javascript
JS+HTML5实现图片在线预览功能
2017/07/22 Javascript
Vue2.0实现将页面中表格数据导出excel的实例
2017/08/09 Javascript
如何使用VuePress搭建一个类型element ui文档
2019/02/14 Javascript
vscode+gulp轻松开发小程序的完整步骤
2020/10/18 Javascript
React实现todolist功能
2020/12/28 Javascript
[44:22]完美世界DOTA2联赛循环赛 FTD vs PXG BO2第一场 11.01
2020/11/02 DOTA
Python 快速实现CLI 应用程序的脚手架
2017/12/05 Python
python按行读取文件,去掉每行的换行符\n的实例
2018/04/19 Python
Django框架设置cookies与获取cookies操作详解
2019/05/27 Python
python画图把时间作为横坐标的方法
2019/07/07 Python
python 判断一组数据是否符合正态分布
2020/09/23 Python
python 爬取百度文库并下载(免费文章限定)
2020/12/04 Python
css3实现二维码扫描特效的示例
2020/10/29 HTML / CSS
学校感恩教育活动总结
2014/07/07 职场文书
学雷锋志愿者活动方案
2014/08/21 职场文书
计划生育汇报材料
2014/12/26 职场文书
个人事迹材料怎么写
2014/12/30 职场文书
2019年描写人生经典诗句大全
2019/07/08 职场文书
使用pandas模块实现数据的标准化操作
2021/05/14 Python