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

一.准备工作

python tkinter

二.预览

1.搜索

2.下载

3.结果

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

三.详细设计

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

四.源代码

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基于tkinter制作无损音乐下载工具(附源码)的详细内容,更多关于python制作音乐下载工具的资料请关注www.887551.com其它相关文章!