网易云音乐推荐歌单API逆向工程:Python模拟加密请求实战

发布时间:2026/8/7 4:41:10
网易云音乐推荐歌单API逆向工程:Python模拟加密请求实战 1. 项目缘起为什么我们需要自己获取推荐歌单的API做音乐类应用或者数据分析的朋友可能都遇到过这样一个需求想获取网易云音乐的歌单数据特别是那些“每日推荐”、“私人雷达”或者“精品歌单广场”里的内容。官方的客户端用起来当然方便但如果你想批量分析歌单风格、做个性化推荐系统、或者为自己的小程序集成一个“今日推荐”模块手动复制粘贴显然不现实。这时候一个稳定、可靠的歌单数据接口就成了刚需。我最近就在做一个音乐偏好分析的小工具核心功能之一就是抓取并分析用户的歌单收藏和平台的推荐内容。网易云音乐作为国内用户基数庞大的平台其推荐算法和歌单质量都很有参考价值。然而官方并没有公开提供一个完整的、面向开发者的歌单推荐API。我们看到的“相关热搜词”里充斥着各种“api error”和接口调用问题这恰恰说明了在非官方环境下调用这类服务的复杂性和普遍存在的坑。所以这篇内容就来聊聊如何通过技术手段相对稳定地获取到网易云音乐的歌单推荐数据。这不是一个简单的“复制某个URL就能用”的教程而是一个需要理解其网络通信机制、应对反爬策略、并妥善处理数据的完整过程。我会结合我实际项目中的踩坑经验把原理、步骤、代码和注意事项都掰开揉碎了讲清楚。2. 核心原理拆解网易云音乐接口的通信机制在动手写代码之前我们必须先搞清楚网易云音乐客户端与服务器是如何交互的。理解了这个我们才能模拟出一个“合法”的客户端去请求数据而不是被轻易地拦截或返回一堆“api error: 400”。网易云音乐的接口并非传统的RESTful API它是一套自研的、经过加密和签名的私有协议。其核心流程可以概括为以下几点2.1 请求的加密与参数构造当你点击“每日推荐歌单”时客户端并不会发送一个明文的GET /api/recommend/playlist这样的请求。相反它会将所有请求参数包括你的用户标识、时间戳、目标接口名等按照特定规则组装成一个字典然后对这个字典进行两次加密。第一次加密AES-128-ECB使用一个固定的密钥对参数字典进行AES加密。这个密钥是公开的在许多开源项目中都能找到。第二次加密RSA将AES加密后的密文再用一个RSA公钥进行加密。这个公钥也是固定的。参数组装最终这个双重加密后的字符串会以params字段名通过POST请求的application/x-www-form-urlencoded格式发送给服务器。同时还会附带一个encSecKey字段它是RSA加密过程中的另一个关键产物。这么做的目的很明确防止请求被轻易篡改和重放增加逆向分析和直接调用的难度。你看到的那些“api error: 400 type must be in...”的错误很可能就是因为params或encSecKey生成不正确服务器解密后得到了非法或残缺的参数。2.2 接口的“伪装”与发现网易云音乐的主要接口域名是music.163.com。通过浏览器的开发者工具F12打开切换到Network网络标签页在客户端进行操作如刷新推荐页可以捕获到真实的网络请求。你会发现不同的功能对应着不同的“接口路径”但它们往往不是描述性的而是像/weapi/v1/discovery/recommend/resource这样的内部代号。这个路径对应的正是“获取每日推荐歌单”。找到这些核心接口路径是我们项目的第一步。这需要一些耐心和尝试或者参考一些维护良好的开源项目。2.3 用户身份与匿名访问对于推荐歌单有两种场景个性化推荐需要用户登录后的Cookie。这包含了你的用户IDuid和登录凭证MUSIC_U。服务器会根据你的听歌历史、收藏等行为生成专属的推荐列表。没有有效Cookie请求这类接口通常会返回默认内容或失败。精品歌单/热门歌单部分公开歌单列表可以不依赖登录Cookie访问但可能会限制频率或返回较少数据。我们的项目将主要聚焦于需要登录态的个性化推荐歌单的获取因为这才是推荐系统的核心价值所在。3. 实战准备环境、工具与关键信息获取理论清楚了我们开始搭建实战环境。这里我选择Python因为它有丰富的库来处理网络请求和加密。3.1 环境与依赖库安装首先确保你安装了Python3。然后我们通过pip安装必要的库pip install requests pycryptodomerequests用于发送HTTP请求比标准库的urllib更简洁高效。pycryptodome一个功能强大的加密解密库我们将用它来实现AES和RSA加密。注意不是pycrypto那个库已停止维护。3.2 获取你的登录Cookie关键步骤这是整个流程中最“手工”但必不可少的一步。Cookie是你的身份凭证模拟请求的核心就是携带正确的Cookie。操作步骤在Chrome或Edge浏览器中打开网易云音乐官网 (music.163.com) 并登录你的账号。按F12打开开发者工具切换到Network(网络) 标签页。刷新页面或在页面内进行一次操作如点击“我的音乐”。在网络请求列表中找到任意一个指向music.163.com的请求通常是第一个document类型的请求。点击该请求在右侧Headers(标头) 标签页中向下找到Request Headers(请求头) 部分。找到Cookie这一行将其值完整地复制出来。它看起来是一长串由分号连接的键值对其中MUSIC_U和__csrf是关键。注意Cookie是敏感信息它等同于你的账号密码。绝对不要将你的真实Cookie提交到公开的Git仓库、分享给他人或写入任何可能泄露的脚本中。在开发时可以将其保存在本地的环境变量或一个不被版本控制的配置文件中。本文后续示例将使用YOUR_COOKIE_HERE作为占位符。3.3 定位目标接口与固定参数通过浏览器开发者工具我们定位到获取“每日推荐歌单”的接口为URL:https://music.163.com/weapi/v1/discovery/recommend/resource方法: POST同时我们需要记录下加密所需的固定密钥这些密钥来自网易云音乐客户端的源代码已被广泛使用AES 密钥:0CoJUm6Qyw8W8judAES 初始向量(IV):0102030405060708RSA 公钥:010001RSA 模数(n):00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e74. 核心代码实现模拟加密与请求发送现在我们把原理和准备工作的碎片拼装成可运行的代码。我将代码分成几个函数便于理解和维护。4.1 实现AES加密函数网易云音乐使用的AES模式是CBC填充方式为PKCS7。我们需要用pycryptodome来实现。from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 import random import string def aes_encrypt(text, key, iv): 使用AES-CBC模式加密文本。 :param text: 要加密的明文字符串 :param key: 密钥字节串 :param iv: 初始向量字节串 :return: Base64编码后的密文字符串 # 确保文本是字节串并进行PKCS7填充 text_bytes pad(text.encode(utf-8), AES.block_size) # 创建AES加密器 cipher AES.new(key, AES.MODE_CBC, iv) # 加密 encrypted_bytes cipher.encrypt(text_bytes) # 返回Base64编码的字符串 return base64.b64encode(encrypted_bytes).decode(utf-8)4.2 实现RSA加密函数RSA加密用于第二次加密。我们使用固定的公钥和模数。from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 import binascii def rsa_encrypt(text, pub_key, modulus): 使用RSA加密文本。 :param text: 要加密的明文字符串 :param pub_key: 公钥十六进制字符串 :param modulus: 模数十六进制字符串 :return: 十六进制格式的密文字符串 # 将文本反转网易云音乐的特殊要求并转换为字节串 text text[::-1] text_bytes text.encode(utf-8) # 将十六进制的模数和公钥转换为整数 n int(modulus, 16) e int(pub_key, 16) # 构造RSA公钥对象 # PKCS#1 v1.5 填充是网易云音乐使用的标准 rsa_key RSA.construct((n, e)) cipher PKCS1_v1_5.new(rsa_key) # 加密 encrypted_bytes cipher.encrypt(text_bytes) # 返回十六进制字符串并确保大写 return binascii.b2a_hex(encrypted_bytes).decode(utf-8).upper()4.3 生成随机字符串与构造加密参数网易云音乐要求params的明文是一个JSON字符串。我们需要构造这个JSON然后进行双重加密。import json import secrets def create_request_params(limit20, offset0): 构造请求参数的明文JSON。 :param limit: 获取歌单的数量 :param offset: 偏移量用于分页 :return: 参数字典 # 这是一个常见的参数结构对应‘/recommend/resource’接口 params_dict { csrf_token: , # 可以从Cookie中获取__csrf字段填入但实测有时可为空 limit: limit, offset: offset, total: True, n: 1000, # 一个内部参数通常固定为1000 type: recommend # 推荐类型 } return params_dict def encrypt_params(params_dict): 对参数字典进行双重加密生成最终的params和encSecKey。 # 固定密钥 aes_key 0CoJUm6Qyw8W8jud aes_iv 0102030405060708 rsa_pub_key 010001 rsa_modulus (00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7 b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280 104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932 575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b 3ece0462db0a22b8e7) # 生成一个16位的随机字符串作为第二次AES加密的密钥 secret_key .join(secrets.choice(string.ascii_letters string.digits) for _ in range(16)) # 将参数字典转换为JSON字符串 text json.dumps(params_dict) # 第一次AES加密使用固定密钥 params aes_encrypt(text, aes_key.encode(utf-8), aes_iv.encode(utf-8)) # 第二次AES加密使用随机密钥 params aes_encrypt(params, secret_key.encode(utf-8), aes_iv.encode(utf-8)) # 对随机密钥进行RSA加密生成encSecKey encSecKey rsa_encrypt(secret_key, rsa_pub_key, rsa_modulus) return params, encSecKey4.4 发送请求并解析响应最后我们组装请求头发送POST请求并处理返回的JSON数据。import requests def get_recommend_playlists(cookie, limit20): 获取每日推荐歌单。 :param cookie: 你的网易云音乐登录Cookie字符串 :param limit: 想要获取的歌单数量 :return: 解析后的歌单列表如果失败返回None url https://music.163.com/weapi/v1/discovery/recommend/resource # 构造请求参数 params_dict create_request_params(limitlimit) params, encSecKey encrypt_params(params_dict) # 构造请求数据 data { params: params, encSecKey: encSecKey } # 构造请求头模拟浏览器 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: https://music.163.com/, Content-Type: application/x-www-form-urlencoded, Cookie: cookie # 这里填入你复制的Cookie } try: response requests.post(url, datadata, headersheaders, timeout10) response.raise_for_status() # 检查HTTP请求是否成功 result response.json() # 检查业务逻辑是否成功 if result.get(code) 200: return result.get(recommend, []) else: print(f请求失败代码: {result.get(code)}, 信息: {result.get(message)}) return None except requests.exceptions.RequestException as e: print(f网络请求出错: {e}) return None except json.JSONDecodeError as e: print(f响应解析JSON出错: {e}) return None # 使用示例 if __name__ __main__: # !!! 重要请将这里的占位符替换为你自己的Cookie !!! my_cookie YOUR_COOKIE_HERE playlists get_recommend_playlists(my_cookie, limit10) if playlists: for idx, pl in enumerate(playlists): print(f{idx1}. {pl.get(name)} - 创建者: {pl.get(creator, {}).get(nickname)} - ID: {pl.get(id)}) else: print(未能获取到推荐歌单。)运行这段代码如果Cookie有效且网络正常你应该能看到控制台打印出你的每日推荐歌单列表包含歌单名、创建者和歌单ID。5. 避坑指南与高级技巧如果你严格按照上面的步骤操作大概率能成功。但真实开发环境远比教程复杂下面是我在项目中遇到的一些典型问题及解决方案。5.1 常见的“api error”分析与解决api error: 400 type must be in [enabled, disabled, auto]这个错误提示非常具体它告诉我们请求参数中的type字段值不合法。在我们构造的params_dict中type字段是recommend。这个错误可能意味着接口已更新网易云音乐的接口参数可能会变动。recommend可能不再是有效值。你需要重新用浏览器抓包查看最新请求中params解密后的明文这需要逆向工程较复杂或者关注相关开源项目的更新。参数构造错误如果加密过程出错服务器解密后得到的乱码可能被解析成非法值。请仔细检查AES和RSA加密的每一个步骤特别是密钥、IV和填充方式是否正确。api error: 400 this models maximum context length is...这个错误信息看起来像来自大型语言模型LLMAPI如OpenAI而不是网易云音乐。这强烈提示你请求可能送错了地方或者你的请求被中间代理、防火墙或某些本地调试工具如Charles、Fiddler错误地转发或修改了。请检查代码中的请求URL是否正确。系统或网络环境是否设置了全局代理将请求导向了别处。本地是否运行了可能拦截HTTPS请求的调试工具。api error: 529 overloaded这是服务器过载或触发频率限制的典型响应。网易云音乐对非官方请求的容忍度有限短时间内发送大量请求极易导致IP或账号被临时限制。解决方案增加延迟在连续请求之间使用time.sleep(random.uniform(2, 5))添加随机延时模拟人类操作。使用代理IP池如果需要进行大规模数据采集必须使用高质量的代理IP来分散请求。尊重robots.txt虽然非官方API不受此约束但遵循基本的网络礼仪避免在高峰时段进行高频请求。5.2 Cookie失效与更新策略Cookie特别是MUSIC_U是有有效期的。可能几天也可能几周后会失效。失效后请求将返回401或400错误提示未登录。如何处理自动化检测在请求函数中加入对返回码的判断。如果遇到401则触发重新登录流程。半自动化更新很难实现全自动登录涉及图形验证码等。一个可行的策略是将Cookie持久化存储如写入文件。每次运行脚本时先读取。当检测到Cookie失效时脚本抛出异常或提示用户并给出手动登录更新Cookie的指引。用户复制新Cookie后脚本更新存储文件。这是安全性和可行性之间的平衡。5.3 数据解析与存储建议成功获取到的歌单数据是一个包含多个字典的列表。每个歌单字典结构丰富例如{ id: 123456789, name: 『私人雷达』每日根据你的口味生成, copywriter: 根据你的音乐口味生成, picUrl: https://p2.music.126.net/...jpg, playCount: 1234567, trackCount: 30, creator: { userId: 88888888, nickname: 网易云音乐 } }存储建议数据库如果需要长期分析建议存入SQLite轻量或MySQL/PostgreSQL。可以设计playlists表存储歌单元数据tracks表存储歌曲信息通过关联表连接。文件对于一次性或小规模分析可以存储为JSON或CSV文件。使用Python的json或pandas库非常方便。去重根据歌单ID (id) 进行去重避免重复存储。5.4 扩展应用获取歌单详情与歌曲列表拿到推荐歌单的ID列表后下一步自然是想获取歌单里的具体歌曲。这需要调用另一个接口/weapi/v3/playlist/detail。其参数构造和加密方式完全一样只是params_dict的结构不同。核心参数是id歌单ID和n歌曲数量1000表示获取所有。def create_playlist_detail_params(playlist_id, limit1000, offset0): params_dict { id: playlist_id, n: limit, offset: offset, total: True, limit: limit, csrf_token: } return params_dict def get_playlist_detail(cookie, playlist_id): url https://music.163.com/weapi/v3/playlist/detail params_dict create_playlist_detail_params(playlist_id) params, encSecKey encrypt_params(params_dict) # 复用之前的加密函数 # ... 后续发送请求的代码与get_recommend_playlists类似 ... # 成功返回后歌曲列表在 result[playlist][tracks] 中注意获取歌单详情尤其是热门歌单同样有频率限制请务必添加延时。6. 项目总结与伦理考量通过以上步骤我们实现了一个能够获取网易云音乐个性化推荐歌单的Python脚本。这个过程本质上是对其客户端通信协议的一种“逆向工程”和模拟。它涉及了网络抓包、参数分析、加密算法复现和请求模拟等多个环节。几点重要的经验与提醒技术是双刃剑这个技术可以用来做有趣的数据分析、个人项目或学习研究。但绝对不要用于恶意爬取、侵犯版权、干扰服务器正常运行或任何商业侵权用途。你的请求会占用服务器资源请保持克制。接口的不稳定性网易云音乐没有公开这些API因此它们随时可能发生变化修改参数、加密方式、甚至关闭接口。你的代码可能需要定期维护更新。关注社区如GitHub上相关的开源项目是获取更新信息的有效途径。账号安全第一Cookie是你的数字身份。泄露Cookie可能导致账号被盗用。务必妥善保管不要在任何公开场合分享。应对反爬的平衡本文介绍的方法目前有效但平台可能会升级反爬措施如增加动态密钥、人机验证等。在工程化项目中需要设计更健壮的机制如自动重试、多账号轮换、验证码识别复杂且可能有法律风险等。这个项目从技术上看是一个很好的学习案例涵盖了现代Web应用逆向中常见的加密、签名和模拟登录问题。从实用角度看它为你打开了一扇窗让你能以编程的方式与喜爱的音乐平台交互创造出属于自己的音乐工具。希望你在使用这些知识时能始终秉持负责任和尊重的态度。