wy音乐下载

查看 119|回复 9
作者:yjhyjh0258   
该下载功能是改编自: https://www.52pojie.cn/thread-2018104-1-1.html
通过获取本地安装的wy云音乐播放列表,自动下载播放列表的歌曲,增加了重复歌曲只保留一首
通过播放列表可以批量多线程下载
[Python] 纯文本查看 复制代码import requests
import re
import hashlib
import json
import os
import csv
import platform
from concurrent.futures import ThreadPoolExecutor
def md5(token):
    """
    计算输入字符串的MD5哈希值。
    """
    return hashlib.md5(token.encode()).hexdigest()
def download_normal_quality(song_id, song_name, folder):
    """
    下载普通音质的歌曲。
    """
    try:
        url = f"https://music.163.com/song?id={song_id}"
        response = requests.get(url)
        html = response.text
        title_match = re.search(r'(.*?)', html)
        if title_match:
            title = title_match.group(1).split('-')[0].strip()
            title = title.replace("/", "、")
        else:
            title = song_name
        download_url = f"http://music.163.com/song/media/outer/url?id={song_id}"
        response = requests.get(download_url, allow_redirects=True)
        file_path = os.path.join(folder, f"{title}.mp3")
        with open(file_path, "wb") as f:
            f.write(response.content)
        return True, f"普通音质下载完成:{file_path}"
    except Exception as e:
        return False, f"普通音质下载失败:{song_id} - {song_name},错误:{e}"
def download_high_quality(song_id, song_name, folder):
    """
    下载高质量(exhigh)的歌曲。
    """
    try:
        token_url = "https://api.toubiec.cn/api/get-token.php"
        response = requests.post(token_url)
        token_data = response.json()
        token = token_data["token"]
        token_md5 = md5(token)
        payload = {
            "url": f"https://music.163.com/#/song?id={song_id}",
            "level": "exhigh",
            "type": "song",
            "token": token_md5
        }
        music_url = "https://api.toubiec.cn/api/music_v1.php"
        headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        }
        response = requests.post(music_url, json=payload, headers=headers)
        data = response.json()
        download_url = data["url_info"]["url"]
        song_name = song_name.replace(":", ":")
        song_artist = data["song_info"]["artist"].replace("/", "、")
        file_suffix = data["url_info"]["type"]
        file_path = os.path.join(folder, f"{song_name} - {song_artist}.{file_suffix}")
        response = requests.get(download_url)
        with open(file_path, "wb") as f:
            f.write(response.content)
        return True, f"高质量下载完成:{file_path}"
    except Exception as e:
        return False, f"高质量下载失败:{song_id} - {song_name},错误:{e}"
def download_song(song, folder):
    """
    下载单首歌曲,优先高质量,失败后尝试普通音质。
    返回:(歌曲信息, 是否成功, 消息)
    """
    song_id = song['id']
    song_name = song['name']
   
    # 优先尝试高质量下载
    success, message = download_high_quality(song_id, song_name, folder)
    if not success:
        # 高质量失败,尝试普通音质
        success, message = download_normal_quality(song_id, song_name, folder)
   
    return song, success, message
def get_song_list():
    """
    从网易云音乐的playingList文件中提取歌曲ID和名称,并返回排序后的列表。
    """
    # 动态获取当前Windows用户名
    username = os.getlogin()
    # 构建playingList文件路径
    playing_list_path = rf"C:\Users\{username}\AppData\Local\Netease\CloudMusic\webdata\file\playingList"
   
    # 检查文件是否存在
    if not os.path.exists(playing_list_path):
        print(f"错误:文件 {playing_list_path} 不存在。")
        print("请确保网易云音乐客户端已安装、登录并打开歌单。")
        exit(1)
   
    try:
        # 读取playingList文件
        with open(playing_list_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        # 提取歌曲ID和名称
        songs = data['list']
        song_list = []
        for song in songs:
            song_id = song['id']
            song_name = song['track']['name']
            song_list.append({'id': song_id, 'name': song_name})
        
        # 按displayOrder排序
        song_list.sort(key=lambda x: songs[[s['id'] for s in songs].index(x['id'])]['displayOrder'])
        
        return song_list
   
    except json.JSONDecodeError as e:
        print(f"JSON解析错误: {e}")
        print("请确保playingList文件为有效的JSON格式。")
        exit(1)
    except Exception as e:
        print(f"发生错误: {e}")
        print("请检查文件路径、权限和网易云音乐客户端状态。")
        exit(1)
def normalize_song_name(song_name):
    """
    规范化歌曲名称,移除括号及内容,转换为小写并去除首尾空格。
    """
    # 移除括号及内容(如 (LIVE)、[Remix] 等)
    song_name = re.sub(r'\s*[\(\[\{].*?[\)\]\}]', '', song_name)
    # 移除常见修饰词(大小写不敏感)
    song_name = re.sub(r'\s*(live|remix|version|edit|mix)\s*', '', song_name, flags=re.IGNORECASE)
    # 转换为小写并去除首尾空格
    return song_name.lower().strip()
def save_failed_downloads(failed_songs):
    """
    将下载失败的歌曲信息保存到CSV文件。
    """
    if failed_songs:
        with open('failed_downloads.csv', 'w', encoding='utf-8', newline='') as f:
            writer = csv.DictWriter(f, fieldnames=['song_id', 'song_name'])
            writer.writeheader()
            for song in failed_songs:
                writer.writerow({'song_id': song['id'], 'song_name': song['name']})
        print(f"下载失败的歌曲已保存到 failed_downloads.csv,共 {len(failed_songs)} 首")
    else:
        print("所有歌曲下载成功,无失败记录")
if __name__ == "__main__":
    # 提示用户确认
    print("本软件只获取本机播放列表歌曲,请确保电脑已经安装网易云音乐,播放列表有歌曲")
    while True:
        user_input = input("是否继续运行?(输入 Y/N):").strip().lower()
        if user_input == "y":
            break
        elif user_input == "n":
            print("用户选择退出,程序关闭。")
            exit(0)
        else:
            print("输入无效,请输入“Y”或“N”。")
   
    # 设置下载文件夹
    download_folder = "downloaded_songs"
    if not os.path.exists(download_folder):
        os.makedirs(download_folder)
   
    # 获取歌曲列表
    songs = get_song_list()
   
    # 初始化计数器
    success_count = 0
    failed_count = 0
    # 记录下载失败的歌曲
    failed_songs = []
    # 记录已处理的歌曲名称(规范化后)
    processed_names = set()
   
    # 过滤重复歌曲
    filtered_songs = []
    for song in songs:
        # 规范化歌曲名称
        normalized_name = normalize_song_name(song['name'])
        
        # 检查是否已处理过同名歌曲
        if normalized_name in processed_names:
            print(f"跳过重复歌曲:{song['id']} - {song['name']}")
            continue
        
        # 添加到已处理集合
        processed_names.add(normalized_name)
        filtered_songs.append(song)
   
    print(f"\n即将下载 {len(filtered_songs)} 首歌曲(已移除重复歌曲)...")
   
    # 使用多线程下载
    with ThreadPoolExecutor(max_workers=5) as executor:
        # 创建下载任务
        futures = [executor.submit(download_song, song, download_folder) for song in filtered_songs]
        
        # 等待所有任务完成并打印结果
        for future in futures:
            song, success, message = future.result()
            print(message)
            if success:
                success_count += 1
            else:
                failed_count += 1
                failed_songs.append(song)
   
    # 显示最终统计信息
    print(f"\n下载完成!")
    print(f"总共尝试 {len(filtered_songs)} 首歌曲")
    print(f"成功下载:{success_count} 首")
    print(f"失败下载:{failed_count} 首")
   
    # 保存失败的歌曲到CSV
    save_failed_downloads(failed_songs)
   
    # 等待用户按任意键关闭
    input("按任意键关闭窗口...")

歌曲, 高质量

shengchang2009   

下载试试
wsck63304521   

我下载来试试看
agdzc   

谢谢,收藏了。
BLENCH00   

现在好多音乐都要版权了,听个歌都不容易。
shane5201   

试了一下挺不错的,只是我vip怎么下载最高的母带有没有办法整一下
yjhyjh0258
OP
  


shane5201 发表于 2025-5-27 14:07
试了一下挺不错的,只是我vip怎么下载最高的母带有没有办法整一下

我只是用了别人的api服务,需要下载最高音质,要去询问原帖
missL123   

收藏收藏,
Mariner   

挺实用的
8204118   

可以无损音质吗?
您需要登录后才可以回帖 登录 | 立即注册

返回顶部