• 游客 你好!
    ✿ MoeBBS 全部交流群公示 ✿
    诋毁本身就是一种仰望👑
  • 论坛资源声明
    1.本论坛所有资源均由用户自行发布,版权归原作者所有,未经允许,禁止非法转载、复制或用于商业用途。
    2.若您认为论坛中的任何资源侵犯了您的合法权益(如版权、肖像权等),请提供相关证明材料通过站内信,邮箱或工单与我们联系,我们将在核实后尽快处理或移除相关内容。
    3.本论坛无法100%保证用户发布内容的准确性、完整性或合法性,使用相关资源前请您自行甄别其风险与适用性,后果由使用者自行承担。
python音乐下载器 (转载)

python音乐下载器 (转载)

✅ Mengta 论坛官方售卖站点
#version 1.1
import os
import sys
import time
import re
import json
import requests


from PyQt5.QtGui import QFont, QIcon
from bs4 import BeautifulSoup
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QListWidget, QListWidgetItem,
QProgressBar, QFileDialog, QMessageBox,
QStatusBar, QCheckBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from requests import Session
from requests.adapters import HTTPAdapter
from urllib3 import Retry


class MusicDownloaderThread(QThread):
"""多线程下载类"""
progress_signal = pyqtSignal(int, int, int) # 当前下载序号, 总数量, 进度百分比
finished_signal = pyqtSignal(int, str, bool) # 索引, 文件名, 是否成功
error_signal = pyqtSignal(str)

def __init__(self, music_list, download_path):
super().__init__()
self.music_list = music_list
self.download_path = download_path
self.session = Session()
self.running = True
self.headers = {
'Referer': '',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
}

def run(self):
"""执行下载任务"""
total = len(self.music_list)
for idx, (name, url) in enumerate(self.music_list):
if not self.running:
break

self.progress_signal.emit(idx + 1, total, 0)
try:
# 获取音乐ID
ask_id = re.compile('')
music_id = re.findall(ask_id, url)[0]

# 获取实际下载链接
music_data = self.session.post(
'',
headers=self.headers,
data={'id': music_id, 'type': 'music'},
timeout=30
).json()

# 下载音乐
music_url = music_data.get('url')
filename = re.sub(r'[\\/*?:"<>|]', "", name) + '.mp3'
filepath = os.path.join(self.download_path, filename)

response = self.session.get(
music_url,
headers={'User-Agent': self.headers['User-Agent']},
stream=True,
timeout=60
)

# 带进度下载
total_size = int(response.headers.get('content-length', 0))
downloaded = 0
with open(filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=1024):
if not self.running:
break
if chunk:
f.write(chunk)
downloaded += len(chunk)
progress = int((downloaded / total_size) * 100) if total_size > 0 else 0
self.progress_signal.emit(idx + 1, total, progress)

if self.running:
self.finished_signal.emit(idx, filename, True)
except Exception as e:
if self.running:
self.finished_signal.emit(idx, name, False)
self.error_signal.emit(f"下载失败: {name} - {str(e)}")

def stop(self):
"""停止下载"""
self.running = False


class MusicDownloaderApp(QMainWindow):
"""音乐下载器主界面"""

def __init__(self):
super().__init__()
# 设置默认下载路径
self.default_path = "E:\\music"
self.setWindowIcon(QIcon("music.ico")) # 支持.ico、.png等格式[3,4,6](@ref)
# self.initUI()

# 尝试从文件加载保存的路径
self.download_path = self.load_saved_path()
os.makedirs(self.download_path, exist_ok=True)

self.init_ui()
self.setup_connections()

# 初始化会话
self.session = Session()
self.session_ask()

def load_saved_path(self):
"""从文件加载保存的下载路径"""
try:
if os.path.exists("download_path.txt"):
with open("download_path.txt", "r") as f:
saved_path = f.read().strip()
if saved_path and os.path.isdir(saved_path):
return saved_path
except:
pass
return self.default_path

def save_path_to_file(self, path):
"""保存路径到文件"""
try:
with open("download_path.txt", "w") as f:
f.write(path)
except Exception as e:
self.show_error(f"保存路径失败: {str(e)}")

def init_ui(self):
"""初始化用户界面"""
self.setWindowTitle("音乐下载器")
self.setGeometry(300, 200, 800, 500)

# 主布局
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QVBoxLayout(main_widget)

# 顶部搜索区域
search_layout = QHBoxLayout()
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("请输入歌曲名称或歌手...")
search_layout.addWidget(self.search_input)

self.search_btn = QPushButton("搜索")
search_layout.addWidget(self.search_btn)

# 下载路径选择
self.path_btn = QPushButton("选择下载路径")
search_layout.addWidget(self.path_btn)

# 显示当前路径
self.path_label = QLabel(f"当前下载路径: {self.download_path}")
search_layout.addWidget(self.path_label)

main_layout.addLayout(search_layout)

# 搜索结果区域
result_group = QVBoxLayout()
result_group.addWidget(QLabel("搜索结果"))
self.result_list = QListWidget()
self.result_list.setSelectionMode(QListWidget.ExtendedSelection) # 多选模式
result_group.addWidget(self.result_list)

# 下载控制
download_control_layout = QHBoxLayout()
self.download_btn = QPushButton("下载选中")
self.select_all_btn = QPushButton("全选")
download_control_layout.addWidget(self.select_all_btn)
download_control_layout.addWidget(self.download_btn)
result_group.addLayout(download_control_layout)

# 批量下载选项
self.batch_download_check = QCheckBox("批量下载模式")
result_group.addWidget(self.batch_download_check)

main_layout.addLayout(result_group, 1)

# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
main_layout.addWidget(self.progress_bar)

# 状态栏 - 版权声明
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
copyright_label = QLabel("该软件仅供学习和娱乐,如有侵权请联系删除")
copyright_label.setFont(QFont("Arial", 8))
self.status_bar.addPermanentWidget(copyright_label)

def setup_connections(self):
"""设置信号连接"""
self.search_btn.clicked.connect(self.search_music)
self.download_btn.clicked.connect(self.download_selected)
self.select_all_btn.clicked.connect(self.select_all_results)
self.path_btn.clicked.connect(self.select_download_path)

def session_ask(self):
"""初始化会话"""
try:
url = ''
self.session.get(url=url, headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
}, timeout=10)
except Exception as e:
self.show_error(f"初始化失败: {str(e)}")

def search_music(self):
"""搜索音乐"""
keyword = self.search_input.text().strip()
if not keyword:
self.show_warning("请输入搜索关键词")
return

self.result_list.clear()
self.search_btn.setEnabled(False)
self.search_btn.setText("搜索中...")

try:
# 配置请求重试策略
retry_strategy = Retry(
total=3, # 最大重试次数
backoff_factor=1, # 指数退避因子
status_forcelist=[500, 502, 503, 504], # 触发重试的状态码
allowed_methods=["GET"] # 只对GET请求重试
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)

play_list = [] # 存储所有<li>元素

for page in range(1, 10):
query_url = f'。。。。/{keyword}/{page}.html'
#print(f"正在请求: {query_url}")

for attempt in range(3): # 每页最多重试3次
try:
response = self.session.get(
query_url,
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
},
timeout=15 # 更合理的超时时间
)
response.raise_for_status() # 检查HTTP状态码[5,9](@ref)

soup = BeautifulSoup(response.text, 'html.parser')
container = soup.find('div', class_='play_list')

# 检查容器是否存在[1,5](@ref)
if container:
query_list = container.find_all('li')
#print(f"第{page}页解析到{len(query_list)}个元素")
play_list.extend(query_list) # 扁平化存储元素
break # 成功则跳出重试循环
else:
print(f"警告:第{page}页未找到play_list容器")

except requests.exceptions.RequestException as e:
print(f"请求异常(尝试{attempt + 1}/3): {e}")
time.sleep(2 ** attempt) # 指数退避等待[9](@ref)
except Exception as e:
print(f"解析异常: {e}")
break
else: # 重试全部失败
pass

status_bar = self.statusBar()

# 检查是否已存在左下角标签
if hasattr(self, 'left_label') and self.left_label is not None:
# 从状态栏移除旧标签
status_bar.removeWidget(self.left_label) # 关键操作[7](@ref)
# 安全销毁旧标签对象
self.left_label.deleteLater()
self.left_label = None

# 创建新标签(带红色字体)
self.left_label = QLabel(f"总共获取到{len(play_list)}个结果")
self.left_label.setStyleSheet("color: red; font: 8pt 'Arial';") # 设置红色字体

# 添加到左下角(左侧区域)
status_bar.addWidget(self.left_label) # 默认左下角[1,7](@ref)

if play_list:
for idx, li in enumerate(play_list, 1):
try:
link = li.find('a')
if link: # 检查<a>标签是否存在[1](@ref)
name = link.text.strip()
href = '' + link['href']
list_item = QListWidgetItem(f"{idx}. {name}")
list_item.setData(Qt.UserRole, (name, href))
self.result_list.addItem(list_item)
except TypeError: # 处理NoneType异常[1](@ref)
print(f"无效的<li>元素: {li}")
else:
self.show_info("未找到相关结果")

except Exception as e:
self.show_error(f"搜索失败: {str(e)}")
import traceback
traceback.print_exc() # 打印完整堆栈[2](@ref)

finally:
self.search_btn.setEnabled(True)
self.search_btn.setText("搜索")

def download_selected(self):
"""下载选中的音乐"""
selected_items = self.result_list.selectedItems()
if not selected_items:
self.show_warning("请先选择要下载的歌曲")
return

# 准备下载列表
download_list = []
for item in selected_items:
name, url = item.data(Qt.UserRole)
download_list.append((name, url))

# 批量下载模式
if self.batch_download_check.isChecked():
self.start_download_thread(download_list)
else:
# 单曲下载模式
for name, url in download_list:
self.start_download_thread([(name, url)])

def start_download_thread(self, download_list):
"""启动下载线程"""
if not download_list:
return

# 创建并启动下载线程
self.download_thread = MusicDownloaderThread(download_list, self.download_path)
self.download_thread.progress_signal.connect(self.update_download_progress)
self.download_thread.finished_signal.connect(self.download_finished)
self.download_thread.error_signal.connect(self.show_error)
self.download_thread.start()

# 显示进度条
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(0)
self.download_btn.setEnabled(False)

def update_download_progress(self, current, total, progress):
"""更新下载进度"""
self.progress_bar.setFormat(f"下载中: {current}/{total} ({progress}%)")
self.progress_bar.setValue(progress)

def download_finished(self, index, filename, success):
"""下载完成处理"""
if success:
self.show_info(f"下载完成: {filename}")

# 检查是否全部完成
if index == len(self.download_thread.music_list) - 1:
self.progress_bar.setVisible(False)
self.download_btn.setEnabled(True)
self.show_info("所有下载任务已完成!")

def select_download_path(self):
"""选择下载路径"""
path = QFileDialog.getExistingDirectory(
self,
"选择下载目录",
self.download_path,
QFileDialog.ShowDirsOnly
)
if path:
self.download_path = path
self.path_label.setText(f"当前下载路径: {path}")
self.save_path_to_file(path)
self.show_info(f"下载路径已设置为: {path}")

def select_all_results(self):
"""全选搜索结果"""
self.result_list.selectAll()

def show_error(self, message):
"""显示错误信息"""
QMessageBox.critical(self, "错误", message)

def show_warning(self, message):
"""显示警告信息"""
QMessageBox.warning(self, "警告", message)

def show_info(self, message):
"""显示信息"""
QMessageBox.information(self, "提示", message)

def closeEvent(self, event):
"""关闭窗口时的处理"""
if hasattr(self, 'download_thread') and self.download_thread.isRunning():
self.download_thread.stop()
self.download_thread.wait()
event.accept()


if __name__ == "__main__":
app = QApplication(sys.argv)
downloader = MusicDownloaderApp()
downloader.show()
sys.exit(app.exec_())

通过网盘分享的文件:音乐下载器 1.0.1.rar
链接: pan.baidu.com 提取码: hrrp

可批量下载,批量下载前需要勾选批量下载框,可自定义保存路径
 

相似主题

  • 文章 文章
写了一个python脚本。方便其他习惯用python的小伙伴们挂青龙面板上。 使用方法在脚本 第70行 跟 第72行 处 填写账号密码即可 。(强烈建议不要设置跟其他平台账号一样的密码!!) [Python] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27...
回复
0
查看
11
公开演示网站: 文档: 调用「一言」API教程 通过简单GET请求获取随机经典语句,支持动画/漫画/影视/诗词等多种分类。 参数说明 ■ 路径参数 path type (必填) 分类代码: ■ 查询参数 query full (选填) 设为true时返回完整对象(包含作者/出处等信息) 默认值:false 请求示例 ▷ cURL请求: ▷ Python请求: ▷...
回复
0
查看
209
  • 红包主题
--- Powered by Sukairain X ChatGPT o3-mini X Deepseek 在你的xenForo论坛使用请遵守MIT的开源规则 ### **一、服务器环境准备** ```bash # 1. SSH 登录服务器 ssh your_username@your_server_ip -p 22 # 2. 进入 XenForo 安装目录(根据实际情况调整) cd...
回复
3
查看
148
后退
顶部 底部