102 lines
3.9 KiB
Python
102 lines
3.9 KiB
Python
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'<li><a href="/song\?id=(\d+)">(.*?)</a>', 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("所有歌曲下载完成!") |