Python-自制简易音乐播放器

2023-12-15 15:34:01


前言

原理简单:通过外链和歌曲Id拼接成下载链接来下载歌曲。


一、代码

做了个ui输入歌单链接:
注意这里歌单的url格式固定:
https://music.163.com/playlist?id=歌单id
在这里插入图片描述


import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QLineEdit, QPushButton, QFormLayout, \
    QPlainTextEdit
from PyQt5.QtCore import Qt, QEventLoop
from PyQt5.QtGui import QIcon
import os
import time
import requests
from lxml import etree

class MusicDownloader(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Music Downloader")
        self.setWindowIcon(QIcon("icon.png"))
        self.setGeometry(600, 600, 700, 700)

        self.url_label = QLabel("URL:")
        self.url_input = QLineEdit()
        self.download_btn = QPushButton("Download")
        self.download_btn.clicked.connect(self.download_music)

        self.output_window = QPlainTextEdit()
        self.output_window.setReadOnly(True)

        layout = QVBoxLayout()
        layout.addWidget(self.url_label)
        layout.addWidget(self.url_input)
        layout.addWidget(self.download_btn)
        layout.addWidget(self.output_window)

        self.setLayout(layout)
        current_file = os.path.realpath(__file__)
        print("下载文件保存位置:", current_file+"\music")
        self.output_window.appendPlainText(f"下载文件保存位置:{current_file}\music")

    def download_music(self):
        url = self.url_input.text()
        if url.startswith("https://music.163.com/playlist?id="):
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
            }
            response = requests.get(url, headers=headers)
            html = etree.HTML(response.text)
            music_label_list = html.xpath('//a[contains(@href,"/song?")]')

            if not os.path.exists('music'):
                os.mkdir('music')

            for music_label in music_label_list:
                href = music_label.xpath('./@href')[0]
                music_id = href.split('=')[1]
                if music_id.isdigit():
                    music_name = music_label.xpath('./text()')[0]
                    music_url = 'http://music.163.com/song/media/outer/url?id=' + music_id
                    response = requests.get(music_url, headers=headers)
                    with open(f'./music/{music_name}.mp3', 'wb') as file:
                        file.write(response.content)
                    self.output_window.appendPlainText(f'《{music_name}》download success')
                    QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
                    time.sleep(1)
        else:
            self.output_window.appendPlainText("Invalid URL")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MusicDownloader()
    window.show()
    sys.exit(app.exec_())

二、代码实现

1.库

导入库:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QLineEdit, QPushButton, QFormLayout, \
    QPlainTextEdit
from PyQt5.QtCore import Qt, QEventLoop
from PyQt5.QtGui import QIcon

2.做ui窗口

代码如下:

在Python中定义一个类,()中传的参数是这个类的父类

class MusicDownloader(QWidget):

init()方法是在类实例化时自动执行的特殊方法。当通过使用类名后面加上括号来创建类的实例时,init()方法会被自动调用。
self指实例化的对象

def __init__(self):

MusicDownloader类继承自QWidget类,而QWidget类是Qt库中的一个基本窗口部件类。调用super().init()实际上是在调用QWidget类的初始化函数,以确保MusicDownloader类的窗口部件正确初始化并获得父类的一些基本功能和属性。

super().__init__()
self.setWindowTitle("Music Downloader") //窗口标题
self.setWindowIcon(QIcon("icon.png"))  //使用名为 "icon.png" 的图标文件作为图标(少用)
self.setGeometry(600, 600, 700, 700) //窗口的位置设置为(600, 600),宽度设置为700,高度设置为700

创建了一个QLabel对象,并将其文本内容设置为"URL:"。QLabel用于显示文本或图像,并且可以在用户界面中作为标签或标识显示某个元素。
在这个代码中,self.url_labelMusicDownloader类的一个属性,用于存储对该QLabel对象的引用。通过这个引用,可以对这个标签进行进一步的设置和操作,比如更改文本内容、设置字体样式等。在用户界面的布局中,可以使用self.url_label来添加、修改或移动这个标签。

self.url_label = QLabel("URL:")

创建了一个QLineEdit对象,即文本输入框。QLineEdit用于接收用户的文本输入,并可以在用户界面中用于输入和编辑文本。
在这个代码中,self.url_input是MusicDownloader类的一个属性,用于存储对该QLineEdit对象的引用。通过这个引用,可以对这个输入框进行进一步的设置和操作,比如获取用户输入的文本、设置默认文本、限制输入格式等。
在用户界面的布局中,可以使用self.url_input来添加、修改或移动这个输入框。可以通过连接到适当的槽函数,来处理这个输入框中的文本输入事件。

self.url_input = QLineEdit()

QLineEdit类提供了设置验证器的setValidator()方法。验证器可以用来指定输入的格式和规则。你可以使用内置的验证器类(如QIntValidator(输入整数)、QDoubleValidator(输入小数))或自定义的验证器类来限制输入。

edit = QLineEdit()
validator = QIntValidator()
edit.setValidator(validator)

QLineEdit还支持掩码功能。通过设置掩码,可以指定输入字段的格式,比如日期、时间、电话号码等。

以下是一个使用掩码限制输入为日期的示例:

edit = QLineEdit()
edit.setInputMask("9999-99-99")

制作一个按钮,按钮中文本内容:“Download”
这个按钮链接到函数download_music

self.download_btn = QPushButton("Download")
self.download_btn.clicked.connect(self.download_music)

QPlainTextEdit是一个多行文本编辑器部件,可以用于显示和编辑多行文本。
输出窗口权限为只读

self.output_window = QPlainTextEdit()
self.output_window.setReadOnly(True)

窗口组件布局:

   //QHBoxLayout() 水平布局
   //这行代码创建了一个垂直布局对象 QVBoxLayout,用于垂直地排列小部件。
    layout = QVBoxLayout()
    //排列部件
    layout.addWidget(self.url_label)
    layout.addWidget(self.url_input)
    layout.addWidget(self.download_btn)
    layout.addWidget(self.output_window)
    //应用布局
    self.setLayout(layout)
//在输出窗口添加文本
self.output_window.appendPlainText(f"下载文件保存位置:{current_file}\music")
class MusicDownloader(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Music Downloader")
        //self.setWindowIcon(QIcon("icon.png"))
        self.setGeometry(600, 600, 700, 700) //位置:(x,y)大小:(长,宽)

        self.url_label = QLabel("URL:") //标签
        self.url_input = QLineEdit() //输入框
        self.download_btn = QPushButton("Download") //按钮
        self.download_btn.clicked.connect(self.download_music) //按钮连接函数

        self.output_window = QPlainTextEdit() //输出框
        self.output_window.setReadOnly(True) //只读权限

        layout = QVBoxLayout() //垂直布局
        layout.addWidget(self.url_label) //布局
        layout.addWidget(self.url_input) //布局
        layout.addWidget(self.download_btn) //布局
        layout.addWidget(self.output_window) //布局

        self.setLayout(layout) //应用布局
        current_file = os.path.realpath(__file__)
        //在输出窗口添加文本
        self.output_window.appendPlainText(f"下载文件保存位置:{current_file}\music")


3爬虫

    def download_music(self):
        url = self.url_input.text()  //获取输入文本
        //判断输入的是不是歌单网址
        if url.startswith("https://music.163.com/playlist?id="):
            headers = {
                    自行添加
            }
            response = requests.get(url, headers=headers)
            html = etree.HTML(response.text)
            //href属性值中包含/song的a标签,中有歌曲的id
            music_label_list = html.xpath('//a[contains(@href,"/song?")]')

            if not os.path.exists('music'):
                os.mkdir('music')

            for music_label in music_label_list:
                href = music_label.xpath('./@href')[0]
                music_id = href.split('=')[1]
                if music_id.isdigit():
                    //歌曲名字就在a标签内部
                    music_name = music_label.xpath('./text()')[0]
                    //拼接url
                    music_url = 'http://music.163.com/song/media/outer/url?id=' + music_id
                    response = requests.get(music_url, headers=headers)
                    with open(f'./music/{music_name}.mp3', 'wb') as file:
                        file.write(response.content)
                    self.output_window.appendPlainText(f'《{music_name}》download success')
                    QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) //循环内部事件时不鸟外部输入
                    time.sleep(1) //爬一个休息一秒
        else:
        //输入的不是歌单Url
            self.output_window.appendPlainText("Invalid URL")

额外的小知识:
在用代码对窗口进行直接操作时:
time.sleep()会导致主线程被阻塞,使得窗口无法响应事件,包括关闭事件。因此,需要避免在主线程中使用time.sleep()来等待。

for i in range(10):
    self.output_window.appendPlainText(i)
    QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
    time.sleep(3)

解决:

    def download_music(self):
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_output) //超时运行self.update_output
        self.counter = 0
        self.timer.start(3000) //设定3秒一次的触发超时运行self.update_output

    def update_output(self):
        self.output_window.appendPlainText(str(self.counter))
        self.counter += 1
        if self.counter >= 10:
            self.timer.stop()

文章来源:https://blog.csdn.net/nobestnobest/article/details/134768575
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。