| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
共有 886 人关注过本帖
标题:求助:修复多平台音乐下载器
只看楼主 加入收藏
王咸美
Rank: 1
等 级:新手上路
帖 子:909
专家分:3
注 册:2018-1-4
收藏
 问题点数:0 回复次数:0 
求助:修复多平台音乐下载器
下列多平台音乐下载器,只有 网易云能勉强使用,酷我、QQ音乐、酷狗均无法运行,请高手赐教,万分感谢!!!
(本人是新手,诚心求教,不喜勿喷!)

import os
import re
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import webbrowser
import requests
from threading import Thread
import time
import random
import hashlib
import json

class MusicDownloader:
    def __init__(self, width=1000, height=650):
        self.root = tk.Tk()
        self.root.title("四平台音乐下载器(最终修复版)")
        self.root.geometry(f"{width}x{height}")
        self.root.resizable(False, False)

        self.keyword = tk.StringVar()
        self.platform = tk.IntVar(value=1)  # 1酷我 2网易云 3QQ 4酷狗
        self.quality = tk.StringVar(value="128k")

        self.tree = None
        self.song_list = []

        self.setup_ui()
        self.center_window(width, height)

    def center_window(self, w, h):
        ws = self.root.winfo_screenwidth()
        hs = self.root.winfo_screenheight()
        x = (ws - w) // 2
        y = (hs - h) // 2
        self.root.geometry(f"{w}x{h}+{x}+{y}")

    def setup_ui(self):
        # ...(UI布局代码保持不变,与上一版本相同)...
        # 菜单
        menu = tk.Menu(self.root)
        self.root.config(menu=menu)
        help_menu = tk.Menu(menu, tearoff=0)
        menu.add_cascade(label="菜单", menu=help_menu)
        help_menu.add_command(label="使用说明", command=lambda: webbrowser.open("https://www.baidu.com"))
        help_menu.add_command(label="退出", command=self.root.quit)

        # 平台选择
        frame1 = tk.Frame(self.root)
        tk.Label(frame1, text="平台:").pack(side=tk.LEFT, padx=5)
        tk.Radiobutton(frame1, text="酷我", variable=self.platform, value=1).pack(side=tk.LEFT, padx=5)
        tk.Radiobutton(frame1, text="网易云", variable=self.platform, value=2).pack(side=tk.LEFT, padx=5)
        tk.Radiobutton(frame1, text="QQ音乐", variable=self.platform, value=3).pack(side=tk.LEFT, padx=5)
        tk.Radiobutton(frame1, text="酷狗", variable=self.platform, value=4).pack(side=tk.LEFT, padx=5)
        frame1.pack(pady=5)

        # 音质
        frame_q = tk.Frame(self.root)
        tk.Label(frame_q, text="音质:").pack(side=tk.LEFT, padx=5)
        (frame_q, textvariable=self.quality, values=["128k", "320k", "flac"], width=10).pack(side=tk.LEFT)
        frame_q.pack(pady=2)

        # 搜索
        frame2 = tk.Frame(self.root)
        tk.Label(frame2, text="歌名/歌手:").pack(side=tk.LEFT, padx=5)
        tk.Entry(frame2, textvariable=self.keyword, width=40).pack(side=tk.LEFT, padx=5)
        tk.Button(frame2, text="搜索", command=self.start_search_thread, bg="#409eff", fg="white").pack(side=tk.LEFT, padx=5)
        frame2.pack(pady=5)

        # 表格
        frame3 = tk.Frame(self.root)
        columns = ("序号", "歌手", "歌名", "专辑", "时长")
        self.tree = ttk.Treeview(frame3, columns=columns, show="headings", height=22)
        for col in columns:
            self.tree.heading(col, text=col)
            self.tree.column(col, width=100 if col=="序号" else 180, anchor="center")
        self.tree.pack(fill=tk.BOTH, expand=True)
        frame3.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)

        # 下载按钮
        frame4 = tk.Frame(self.root)
        tk.Button(frame4, text="下载选中", command=self.download_selected, bg="#67c23a", fg="white").pack(side=tk.LEFT, padx=10)
        tk.Button(frame4, text="批量下载本页", command=self.batch_download, bg="#e6a23c", fg="white").pack(side=tk.LEFT, padx=10)
        frame4.pack(pady=10)

    # ================== 线程安全的UI更新 ==================
    def safe_tree_insert(self, values):
        self.root.after(0, lambda: self.tree.insert("", "end", values=values))

    def safe_show_message(self, title, msg, kind="info"):
        def show():
            if kind == "info":
                messagebox.showinfo(title, msg)
            elif kind == "warning":
                messagebox.showwarning(title, msg)
            elif kind == "error":
                messagebox.showerror(title, msg)
        self.root.after(0, show)

    def safe_tree_clear(self):
        self.root.after(0, lambda: [self.tree.delete(i) for i in self.tree.get_children()])

    # ================== 搜索入口 ==================
    def start_search_thread(self):
        kw = self.keyword.get().strip()
        if not kw:
            self.safe_show_message("提示", "请输入关键词", "warning")
            return
        Thread(target=self.do_search, daemon=True).start()

    def do_search(self):
        self.safe_tree_clear()
        self.song_list.clear()
        kw = self.keyword.get().strip()
        p = self.platform.get()

        try:
            if p == 1:
                self.search_kuwo(kw)
            elif p == 2:
                self.search_netease(kw)
            elif p == 3:
                self.search_qq(kw)
            elif p == 4:
                self.search_kugou(kw)
            if not self.song_list:
                self.safe_show_message("提示", "没有搜索到相关歌曲", "info")
        except Exception as e:
            self.safe_show_message("错误", f"搜索失败:{str(e)}", "error")

    # ================== 1. 酷我 (Kuwo)  ==================
    def search_kuwo(self, kw):
        session = requests.Session()
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0",
            "Referer": "https://,
            "Origin": "https://
        }

        token = "Fp4lRrKJ2LmN3pQw"  # 通用token
        headers["csrf"] = token
        headers["Cookie"] = f"kw_token={token}"

        url = "https://www.
        params = {"key": kw, "pn": 1, "rn": 30, "httpsStatus": 1}

        try:
            res = session.get(url, params=params, headers=headers, timeout=10).json()
            songs = res.get("data", {}).get("list", [])
            for i, s in enumerate(songs):
                self.safe_tree_insert((i+1, s.get("artist", ""), s.get("name", ""), s.get("album", ""), ""))
                self.song_list.append({
                    "type": "kuwo", "id": s.get("rid"),
                    "name": s.get("name", ""), "artist": s.get("artist", "")
                })
        except Exception as e:
            print(f"酷我搜索异常: {e}")

    # ================== 2. 网易云 (Netease)  ==================
    def search_netease(self, kw):
        url = "https://music.
        params = {"s": kw, "type": 1, "limit": 30, "offset": 0}
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0",
            "Referer": "https://music.,
            "Cookie": "_iuqxldmzr_=32; _ntes_nnid=70b45e8ac49f0009882cdc85f2dbc4a1,1776222084257; _ntes_nuid=70b45e8ac49f0009882cdc85f2dbc4a1; NMTID=00Oq60uB016Bdgsw0JGhqLiIEZDEagAAAGdjxYQvA; WEVNSM=1.0.0; WNMCID=jkpvho.1776222091815.01.0; ntes_utid=tid._.sEHsk6TKPMVAU0UFUAaS54XvRmlObXMB._.0; sDeviceId=YD-a%2FIEZhpUUzpFBlQFUFOHo9TvEj1faHIF; WM_TID=73WR6veDAypEQFBAERbX4pDvFm1bkJFi; __snaker__id=Q0syMXr8fQlCJmia; gdxidpyhxdE=X4l%2FPWSKUBViiQaKvLdfDwuVItrhSa5Mr%2BWsjUppMNDZRPzZvN7wGpRD8so%2F8WC%5CX8576KOG%2BIQIoce%2BqqDnC3iGKN3IX17ciEvm1OhCfIgMpAKGji%5CYtDNxomRU3p0cEpffIshIxrEyv065jHL9qKtEtC6m0y%2F1DJisB1g%2Bz4X36isg%3A1776223006427; MUSIC_U=0033F87DE3FDDA24D91DAC42B7B27E55EF416FF3721663443320A6BF4FB7F33EC9A1F38694657B7E554E15B8C4F7BB4F4D59F99FA6DB648FE660818A4319BE882C073821ADF6920C5B9F8A3820831347509E4AFD836E1C5C12EC36F937835675087E8D8435F13C2B3EAD72357A7CC0F8220B3F12B4DA2FB26F7A7564A5CB7207F9BAE7DFB199D2A01E46DDA394B76F2CD3F07B334FCCB5577BAAE5698590DD4AB1BE8DE64A6419E0B679472F0E7C38F375EEE5ECE4D5A0DA10BCF2663B22A659BEAFBAD5698D97E61CC40B9F833578E7508651429758914CAB816D3EB3EF71216D901060035B620E06001CE45F984F0FC07EE51D0320ED0B5F4943830F10CBEE642D4FA44052FC0C6BB518FF11E2DE94676BA5B4231CB607DE516D5919E108AA535021026256776D228DC91DCAFF661EC4F955E1EF3ED7097D94380C2C57873A4497435D72F4298B4A14B239CE954CAA23CFDFB06673B95D442C38408761726150611A415525CAE3D046113408BD867F06697BAC3F45CD9A2661ED4316713D12E8401CFB368E5E523218F71CC22FDF656BB6B0F6A11CABEA7B970792A5B705CC99; __csrf=3e372ad5cd968d847a1382ba525f9312; ntes_kaola_ad=1; JSESSIONID-WYYY=SKU%5CZuQfUYFsSVNWPFxPBZtSv8IOKFJVreOqyD3xYlIPfBjJueRvk%2B7aOgeMIwD2u%5C6qdoSfTff06tEQ1HHQSE%2BXEWq2JR%5CIRge9jVBlX9l033%5CpNeKvb%2FB9FK5ecSJQk6PxkBbTbAEyYPBU%5CsmSdf94VsJ%2B%2Fpgj7kPKfx8Hf57ACwcy%3A1776409492544; Hm_lvt_1483fb4774c02a30ffa6f0e2945e9b70=1776222091,1776407693; HMACCOUNT=6C051FAE07DABF5F; WM_NI=xQnk8BmW2YdIuvW%2FZ0e4FgLn0p9Zu218rglr6BkIJzDCGt0J%2FScCbzg8fjNxTRkhaLITaQJt9%2BiUb%2Bd6sdwraYLAVXwy1zeaS2rjbmpdcAXSOpIWGKLIx%2FHuVVcrDiiaeng%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6ee8de461b2ebaab5d94bf1bc8aa6d55b878f9b83c234fbbb87b4cb74b0b7b693d32af0fea7c3b92abc8fa28cbb4aaf89febae743b7aa99d0d93ca7ec8a88cf66f4b0f7bbf83af3e8be88ef7af69ea5aac972889d8995b342ed8dada4e172a5e8aa8bcd74988ea589e4488b8cafb2c980fbf5a7b6e121a8eabd85f67aa38784a2d554b5a7ff9be86b91eebe8be167b4bcada2db598db087b8c6419097b8d0cb7f968aad93c763abbfab8fe637e2a3; Hm_lpvt_1483fb4774c02a30ffa6f0e2945e9b70=1776407735"
        }
        try:
            res = requests.post(url, data=params, headers=headers, timeout=10).json()
            songs = res.get("result", {}).get("songs", [])
            for i, s in enumerate(songs):
                name = s.get("name", "")
                artist = s.get("artists", [{}])[0].get("name", "")
                album = s.get("album", {}).get("name", "")
                self.safe_tree_insert((i+1, artist, name, album, ""))
                self.song_list.append({
                    "type": "netease", "id": s.get("id"),
                    "name": name, "artist": artist
                })
         except Exception as e:
            print(f"网易云搜索异常: {e}")

    # ================== 3. QQ音乐 ==================
    def search_qq(self, kw):
        url = "https://c.y.
        params = {"w": kw, "format": "json", "p": 1, "n": 30}
        headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0",
            "Referer": "https://cn.,
            'Cookie':'pgv_pvid=8035143406; qq_domain_video_guid_verify=64d4a72c5e0a5937; _qimei_uuid42=1a3070a0e2a100d79d01f20a86a53bbbca72b9fdd2; _qimei_fingerprint=5de5aa2c0706f4723398e79048ea8c6e; _qimei_h38=19c54d0b9d01f20a86a53bbb0200000241a307; _qimei_i_3=5cef24d6c45c56dcc197f738598570b4f1efacf9130857d7e5897c582ec07138676a62943c89e2b885ac; _qimei_q32=4ab2cb2cba9268490efda42af3843cc9; _qimei_q36=4b595b61524338d549255b9030001a61a210; _qimei_i_2=58c95f86c75f59de9391ff35598070e3ffbaa1f51b590687e686285b2693206d306536c43a88e2bbaaa8; _qimei_i_1=71c955839c0955df9597ac390f8472b6f5eef5f9140a0681e6dd7a582493206c6163359139d8e1dcd182c3c2; fqm_pvqid=1fc55efa-e3cd-495a-a05f-10c10631ef98; ts_refer=cn. ts_uid=9448146472; RK=0aqfi3zl8a; ptcz=b1b31d1eeb3bfd3ef9502526daaf5a5571feb62b7855a3f8a67a6c99700f0c3c; wxopenid=; music_ignore_pskey=202306271436Hn@vBj; euin=ow4ANeoPNeoF7c**; tmeLoginType=2; psrf_qqrefresh_token=2C93D909767D56175EDFE5AD52466C5C; psrf_access_token_expiresAt=1781585793; psrf_qqopenid=F8D1359FD5B671B64324318A6C77631D; uin=2528348386; qqmusic_key=Q_H_L_63k3NY-RST7MAbgkb4Vaw2dHO10kR7moJvwbQdU4gA742wwBCQ-0aQ_AF0gyGOU4uzU9nQXRcz0GnzNvi7LGSyYNWFe0d1g; wxunionid=; psrf_qqunionid=C8BD2B972C4C1D555A6F41A401DB163A; wxrefresh_token=; qm_keyst=Q_H_L_63k3NY-RST7MAbgkb4Vaw2dHO10kR7moJvwbQdU4gA742wwBCQ-0aQ_AF0gyGOU4uzU9nQXRcz0GnzNvi7LGSyYNWFe0d1g; psrf_musickey_createtime=1776401793; psrf_qqaccess_token=5E6D10A4E32D288C68262ECFB9045BE2; fqm_sessionid=ce4dd248-d4b3-4ae9-a254-f9f258b8e40c; pgv_info=ssid=s3944066700; ts_last=y.'
        }
        try:
            res = requests.get(url, params=params, headers=headers, timeout=10).json()
            songs = res.get("data", {}).get("song", {}).get("list", [])
            for i, s in enumerate(songs):
                name = s.get("songname", "")
                artist = s.get("singer", [{}])[0].get("name", "")
                album = s.get("albumname", "")
                self.safe_tree_insert((i+1, artist, name, album, ""))
                self.song_list.append({
                    "type": "qq", "id": s.get("songmid", ""),
                    "name": name, "artist": artist
                })
        except Exception as e:
            print(f"QQ音乐搜索异常: {e}")

    # ================== 4. 酷狗 (Kugou)  ==================
    def search_kugou(self, kw):
        clienttime = int(round(time.time() * 1000))
        # 生成设备标识
        mid = hashlib.md5(f"kugou_{clienttime}_{random.random()}".encode()).hexdigest()
        # 构建签名
        sign_str = f"keyword={kw}&page=1&pagesize=30&clienttime={clienttime}&mid={mid}"
        signature = hashlib.md5(sign_str.encode()).hexdigest()
        url = "https://complexsearch.
        params = {
            "keyword": kw, "page": 1, "pagesize": 30,
            "srcappid": "2919", "clientver": "1000",
            "clienttime": clienttime, "mid": mid, "uuid": mid,
            "signature": signature
        }
        headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0", "Referer": "https://www.}
        try:
            res = requests.get(url, params=params, headers=headers, timeout=10)
            # 处理JSONP
            text = res.text
            if text.startswith("callback123("):
                text = text[11:-2]
            data = json.loads(text)
            songs = data.get("data", {}).get("lists", [])
            for i, s in enumerate(songs):
                name = s.get("SongName", "")
                artist = s.get("SingerName", "")
                album = s.get("AlbumName", "")
                self.safe_tree_insert((i+1, artist, name, album, ""))
                self.song_list.append({
                    "type": "kugou", "hash": s.get("FileHash", ""),
                    "name": name, "artist": artist
                })
        except Exception as e:
            print(f"酷狗搜索异常: {e}")

    # ================== 获取播放链接 ==================
    def get_song_url(self, song):
        q = self.quality.get()
        try:
            # ---------- 酷我 ----------
            if song["type"] == "kuwo":
                session = requests.Session()
                headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0", "Referer": "https://}
                # 使用通用token
                token = "Fp4lRrKJ2LmN3pQw"
                headers["csrf"] = token
                headers["Cookie"] = f"kw_token={token}"
                url = f"https://antiserver.{song['id']}&format=mp3"
                resp = session.get(url, headers=headers, allow_redirects=False, timeout=10)
                if resp.status_code == 302:
                    return resp.headers.get("Location")
                return None

            # ---------- 网易云 ----------
            elif song["type"] == "netease":
                official = f"https://music.{song['id']}.mp3"
                try:
                    head = requests.head(official, allow_redirects=True, timeout=8)
                    if head.status_code == 200:
                        return official
                except:
                    pass
                # 备用:使用第三方API(如果还能用的话)
                try:
                    api = "https://api.
                    params = {"mid": song["id"], "type": "url"}
                    resp = requests.get(api, params=params, timeout=10)
                    data = resp.json()
                    if data.get("code") == 1 and data.get("data", {}).get("url"):
                        return data["data"]["url"]
                except:
                    pass
                return None

            # ---------- QQ音乐 ----------
            elif song["type"] == "qq":
                if not song["id"]:
                    return None
                # 先获取歌曲详情,提取 media_mid
                detail_url = "https://c.y.
                params = {"songmid": song["id"], "format": "json", "platform": "yqq"}
                headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0", "Referer": "https://y.}
                detail_res = requests.get(detail_url, params=params, headers=headers, timeout=10)
                detail_data = detail_res.json()
                song_data = detail_data.get("data", {}).get("list", [])
                if song_data:
                    media_mid = song_data[0].get("mid", "")
                    if media_mid:
                        # 128k使用M500,320k使用M800,flac使用F000
                        prefix = {"128k": "M500", "320k": "M800", "flac": "F000"}.get(q, "M500")
                        return f"https://dl.stream.qqmusic.{prefix}{media_mid}.mp3?vkey=0&guid=0&fromtag=66"
                # 备用:使用第三方API
                try:
                    api = "https://api.
                    params = {"mid": song["id"], "quality": q.replace("k", "")}
                    resp = requests.get(api, params=params, timeout=10)
                    data = resp.json()
                    if data.get("code") == 1 and data.get("data", {}).get("url"):
                        return data["data"]["url"]
                except:
                    pass
                return None

            # ---------- 酷狗 ----------
            elif song["type"] == "kugou":
                if not song["hash"]:
                    return None
                url = "https://www. + song["hash"]
                headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0","Referer": "https://www.}
                resp = requests.get(url, headers=headers, timeout=10)
                data = resp.json()
                if data.get("status") == 1:
                    play_url = data.get("data", {}).get("play_url")
                    if play_url:
                        return play_url
                return None
        except Exception as e:
            print(f"获取播放地址异常 [{song['name']}]: {e}")
            return None

    # ================== 下载逻辑 ==================
    def download_selected(self):
        sel = self.tree.selection()
        if not sel:
            self.safe_show_message("提示", "请先选择歌曲", "warning")
            return
        idx = self.tree.index(sel[0])
        if idx < len(self.song_list):
            song = self.song_list[idx]
            Thread(target=self.do_download, args=(song,), daemon=True).start()
        else:
            self.safe_show_message("错误", "未找到对应的歌曲信息", "error")

    def batch_download(self):
        if not self.song_list:
            self.safe_show_message("提示", "暂无歌曲可下载", "info")
            return
        num = simpledialog.askinteger("批量下载", f"共{len(self.song_list)}首,下载前几首?",
                                      minvalue=1, maxvalue=len(self.song_list))
        if not num:
            return
        def batch_worker():
            for song in self.song_list[:num]:
                self.do_download(song)
        Thread(target=batch_worker, daemon=True).start()

    def do_download(self, song):
        url = self.get_song_url(song)
        if not url:
            self.safe_show_message("失败", f"{song['name']} 无法获取播放地址", "error")
            return

        save_dir = "./MusicDownload"
        os.makedirs(save_dir, exist_ok=True)

        name = re.sub(r'[\\/*?:"<>|]', "", song["name"])
        artist = re.sub(r'[\\/*?:"<>|]', "", song["artist"])
        ext = "flac" if self.quality.get() == "flac" else "mp3"
        path = os.path.join(save_dir, f"{name} - {artist}.{ext}")

        if os.path.exists(path):
            self.safe_show_message("提示", f"{name} 已存在,跳过下载", "info")
            return

        try:
            headers = {"User-Agent": "Mozilla/5.0"}
            r = requests.get(url, headers=headers, stream=True, timeout=30)
            r.raise_for_status()
            with open(path, "wb") as f:
                for chunk in r.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)
            self.safe_show_message("完成", f"下载成功:\n{name}", "info")
        except Exception as e:
            self.safe_show_message("错误", f"{name} 下载失败:{str(e)}", "error")

    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    app = MusicDownloader()
    app.run()
搜索更多相关主题的帖子: data name if get url 
2026-04-17 16:19
快速回复:求助:修复多平台音乐下载器
数据加载中...
 
   
关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.027857 second(s), 9 queries.
Copyright©2004-2026, BCCN.NET, All Rights Reserved