import re
import os
import requests
import time
# 修改存储目录为 F:\测试
filename = r'F:\测试\音乐\\'
# 如果没有则创建文件夹
if not os.path.exists(filename):
os.makedirs(filename)
# 请求网址
url = 'https://music.163.com/playlist?id=3778678'
# 伪造请求头
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
}
# 发送请求
print("正在获取列表...")
response = requests.get(url, headers=headers)
# 提取数据
html_data = re.findall(r'
(.*?)', response.text)
# 限速设置:30mbps = 30 * 1024 * 1024 / 8 = 3932160 字节/秒
MAX_SPEED_BPS = 30 * 1024 * 1024 // 8 # 约3.75MB/s
# 下载
for i, (num_id, title) in enumerate(html_data, 1):
try:
# 方法1:替换所有Windows文件名中的非法字符
# Windows文件名不能包含的字符: \ / : * ? " < > |
clean_title = re.sub(r'[\\/*?:"<>|]', '', title)
# 方法2:更严格的清理,只保留字母、数字、中文、空格和一些常用标点
# clean_title = re.sub(r'[^a-zA-Z0-9\u4e00-\u9fa5\s\-_\(\)\[\]\.]', '', title)
# 如果清理后文件名为空,使用歌曲ID作为文件名
if not clean_title.strip():
clean_title = f"song_{num_id}"
# 检查文件是否已存在
file_path = os.path.join(filename, clean_title + '.mp3')
if os.path.exists(file_path):
print(f"跳过已存在的文件: {clean_title}")
continue
print(f"正在下载 ({i}/{len(html_data)}): {clean_title} (原标题: {title})")
# 调用接口
music_url = f'https://music.163.com/song/media/outer/url?id={num_id}.mp3'
# 发送请求获取响应(使用流模式)
with requests.get(music_url, headers=headers, stream=True) as response:
response.raise_for_status()
# 获取文件大小
total_size = int(response.headers.get('content-length', 0))
# 保存文件
start_time = time.time()
downloaded_size = 0
with open(file_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192): # 8KB chunks
if chunk:
f.write(chunk)
downloaded_size += len(chunk)
# 计算当前速度
elapsed_time = time.time() - start_time
if elapsed_time > 0:
current_speed = downloaded_size / elapsed_time # 字节/秒
# 如果速度超过限制,则等待
if current_speed > MAX_SPEED_BPS and downloaded_size < total_size:
expected_time = downloaded_size / MAX_SPEED_BPS
sleep_time = expected_time - elapsed_time
if sleep_time > 0:
time.sleep(sleep_time)
# 显示进度
if total_size > 0:
percent = downloaded_size / total_size * 100
print(f"\r进度: {percent:.1f}% ({downloaded_size//1024}KB/{total_size//1024}KB)", end='')
print() # 换行
print(f"完成: {clean_title}")
except Exception as e:
print(f"下载失败 {title}: {str(e)}")
# 如果下载失败,删除可能不完整的文件
if 'file_path' in locals() and os.path.exists(file_path):
os.remove(file_path)
# 添加短暂延迟,避免请求过快
time.sleep(0.5)
print("所有歌曲下载完成!")