当前位置: 首页 > 工具软件 > Music Browser > 使用案例 >

Python用100行代码实现music installer(带GUI)

安奇
2023-12-01

提示:仅供学习使用!

思路

首先声明,我定义了几个全局变量,在开头提一嘴,以免后面看的难受:

list_end = []#用于储存音乐信息列表,后面将其放于listbox中

song_name = ''#音乐名字

先浅做一下GUI,我也是第一次学这个做的不好

def create_frame():

    root = tk.Tk()
    root.title("音乐下载器")
    root.geometry('1000x600')
    root.resizable(height=False,width=False)

    l = Label(root,text = "音乐下载器",bg='#dde5ec',font=('Arial',12),width=15,height=2)
    l.pack()
    l2 = Label(root, text="请输入音乐名:",  font=('Arial', 12), width=15, height=2).place(x=100,y = 90)
    t = Entry(root)

    t.place(x=250, y=100, width=600)

    def get_song_name():
        
    b1 = Button(root,text='搜索',command=get_song_name)
    b1.place(x=860,y=98,height=24)
    lb = Listbox(root,bg='#afb1b3',listvariable=var,borderwidth=5,font=('heiti',16))
        def to_browser():
        
    lb.place(x=180, y=140, height=400, width=600)
    b2 = Button(root,text='跳\n转\n页\n面\n至\n网\n页',font=('Arial',12),bg='#dde5ec',command=to_browser).place(x=782,y=140,height=400,width=50)

    root.mainloop()

分析需求,需要通过在Entry中输入音频名对其进行搜索,获取音频信息列表和音频链接
因此在搜索按钮b1点击时要触发指令获取音频名字,上面面代码中的get_song_name()中应写出以下代码:

	global song_name,list_end
    list_end.append('搜索中......')
    song_name = t.get()
    

获取音频名之后,将名字传入函数get_song_list以获得音频列表,在上方代码后加上代码:

	song_list = get_song_list(song_name=song_name)

获得的音频信息列表song_lsit,需要对其分别进行数据解析和抓取,这里通过一个函数insert_song对song_list分别处理得到信息列表和音频信息:

	thread1 = threading.Thread(name='t1', target=insert_song(song_list))
    thread1.Daemon = True
    thread1.start()

由于是阻塞操作,在这里以多线程的方式执行,并将Daemon设置为True以保证子线程在主线程关闭后关闭。
下面给出上面用到的几个函数:

def get_song_list(song_name):
    # 定义song_name储存歌曲名字
    global list_end
    list_end = ['搜索中......']
    url = f"https://******/search-{song_name}.htm"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
    }
    response = requests.get(url=url, headers=headers)
    response.encoding = "utf-8"
    response = response.text
    # 解析数据
    html = etree.HTML(response)
    song_list = html.xpath('//*[@id="body"]/div/div/div/div[2]/div[2]/ul/li')
    return song_list
def insert_song(song_list):

    for song in song_list:
        global list_end
        list_end.append(from_song_get_info(song)+'#'+from_song_get_url(song))
    list_end.append('搜索完成')

由于listbox在放置后就无法用insert方法插入条目信息了,所以这里用list_end暂时存放,listbox用listvariable = var的方式,并定时更新var的值为list_end的值:

	var = tk.StringVar()
    #用定时器更新var值
    var = updateVar(var=var)
def updateVar(var):
    var.set(list_end)
    threading.Timer(0.1,updateVar,(var,)).start()
    return var

这样一来音频信息的增加和插入可以互相不冲突,同时进行。
由于上面b1的指令存在嵌套函数现象,担心阻塞主线程,故也采用多线程方式:

    def get_song_name():
        def zi():
            global song_name,list_end
            list_end.append('搜索中......')
            song_name = t.get()
            song_list = get_song_list(song_name=song_name)
            thread1 = threading.Thread(name='t1', target=insert_song(song_list))
            thread1.Daemon = True
            thread1.start()

        thread2 = threading.Thread(name='t2', target=zi)
        thread2.Daemon = True
        thread2.start()

我也不知道这样写有没有问题(我也没在别人那见过这样写的),如果有不妥之处,还希望大家指出。
这里再给出上面插入条目信息时使用到的函数:

def from_song_get_url(song):
    detail_url = song.xpath('./div/div[1]/a/@href')[0]
    detail_url = "https://www.hifini.com/" + detail_url
    detail_html = requests.get(detail_url)
    detail_html.encoding = "utf-8"
    detail_html = detail_html.text
    ed = "url: '(.*?)',"
    findall = re.findall(ed, detail_html)
    if len(findall)!= 0:
        findall = findall[0]
    else:
        return "查无链接"
    song_url = 'https://******/' + findall
    return song_url


def from_song_get_info(song):
    # 获取a标签下所有文本
    info = "歌曲信息: " + song.xpath('./div/div[1]/a')[0].xpath('string(.)').strip()
    return info

至此搜索解析操作完成。
下面在b2上添加指令to_browser,当我们选中一条音频信息时,点击按钮b2,帮助我们跳转到浏览器,可用于试听或下载。本来想用selenium实现,但考虑到浏览器版本问题,于是在这里使用cmd命令方式:

    def to_browser():
        def zi2():
            song_url = lb.get(lb.curselection())
            song_url = ''.join(song_url)
            song_url = song_url.split('#')[1]
            os.system('start ' + song_url)
        if ''.join((lb.get(lb.curselection()))) != '查无链接' and ''.join((lb.get(lb.curselection()))) != '':
            thread3 = threading.Thread(name='t3', target=zi2)
            thread3.Daemon = True
            thread3.start()

由于lb.get(lb.curselection())获得的是元祖形式的信息,没法用split方法分隔,因此首先用song_url = ‘’.join(song_url)转成字符串,随后可对其进行判断是否存在,如果存在则启动线程执行操作

就在我以为结束了的时候,发现了一个小问题:在跳转至浏览器时屏幕会闪过dos窗口,影响使用体验,在查找资料后,将os.system换成下面的代码:

os.popen('start ' + song_url)

这样就不会弹出dos窗口了,咱们的**也基本上大功告成了。

源码

import requests
import os
import re
import threading
import time
import tkinter as tk
from tkinter import *
from lxml import etree

list_end = []

song_name = ''
def insert_song(song_list):

    for song in song_list:
        global list_end
        list_end.append(from_song_get_info(song)+'#'+from_song_get_url(song))
    list_end.append('搜索完成')


def get_song_list(song_name):
    # 定义song_name储存歌曲名字
    #song_name = 'hello'
    global list_end
    list_end = ['搜索中......']
    url = f"https://www.hifini.com/search-{song_name}.htm"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
    }
    response = requests.get(url=url, headers=headers)
    response.encoding = "utf-8"
    response = response.text
    # 解析数据
    html = etree.HTML(response)
    song_list = html.xpath('//*[@id="body"]/div/div/div/div[2]/div[2]/ul/li')
    return song_list

def updateVar(var):
    var.set(list_end)
    threading.Timer(0.1,updateVar,(var,)).start()
    return var
def create_frame():

    root = tk.Tk()
    root.title("音乐下载器")
    root.geometry('1000x600')
    root.resizable(height=False,width=False)
    var = tk.StringVar()

    #用定时器更新var值
    var = updateVar(var=var)
    l = Label(root,text = "音乐下载器",bg='#dde5ec',font=('Arial',12),width=15,height=2)
    l.pack()
    l2 = Label(root, text="请输入音乐名:",  font=('Arial', 12), width=15, height=2).place(x=100,y = 90)
    t = Entry(root)

    t.place(x=250, y=100, width=600)

    def get_song_name():
        def zi():
            global song_name,list_end
            list_end.append('搜索中......')
            song_name = t.get()
            song_list = get_song_list(song_name=song_name)
            thread1 = threading.Thread(name='t1', target=insert_song(song_list))
            thread1.Daemon = True
            thread1.start()

        thread2 = threading.Thread(name='t2', target=zi)
        thread2.Daemon = True
        thread2.start()


    b1 = Button(root,text='搜索',command=get_song_name)
    b1.place(x=860,y=98,height=24)
    lb = Listbox(root,bg='#afb1b3',listvariable=var,borderwidth=5,font=('heiti',16))

    def to_browser():
        def zi2():
            song_url = lb.get(lb.curselection())
            song_url = ''.join(song_url)
            song_url = song_url.split('#')[1]
            os.popen('start ' + song_url)
        if ''.join((lb.get(lb.curselection()))) != '查无链接' and ''.join((lb.get(lb.curselection()))) != '':
            thread3 = threading.Thread(name='t3', target=zi2)
            thread3.Daemon = True
            thread3.start()
       
    lb.place(x=180, y=140, height=400, width=600)
    b2 = Button(root,text='跳\n转\n页\n面\n至\n网\n页',font=('Arial',12),bg='#dde5ec',command=to_browser).place(x=782,y=140,height=400,width=50)

    root.mainloop()



def from_song_get_url(song):
    detail_url = song.xpath('./div/div[1]/a/@href')[0]
    detail_url = "https://www.hifini.com/" + detail_url
    detail_html = requests.get(detail_url)
    detail_html.encoding = "utf-8"
    detail_html = detail_html.text
    ed = "url: '(.*?)',"
    findall = re.findall(ed, detail_html)
    if len(findall)!= 0:
        findall = findall[0]
    else:
        return "查无链接"
    song_url = 'https://www.hifini.com/' + findall
    return song_url


def from_song_get_info(song):
    # 获取a标签下所有文本
    info = "歌曲信息: " + song.xpath('./div/div[1]/a')[0].xpath('string(.)').strip()
    return info

if __name__ == '__main__':
     create_frame()
    

 类似资料: