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。这包含了你的用户ID(
uid)和登录凭证(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 密钥:
0CoJUm6Qyw8W8jud - AES 初始向量(IV):
0102030405060708 - RSA 公钥:
010001 - RSA 模数(n):
00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7
4. 核心代码实现:模拟加密与请求发送
现在,我们把原理和准备工作的碎片拼装成可运行的代码。我将代码分成几个函数,便于理解和维护。
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(limit=20, offset=0): """ 构造请求参数的明文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, limit=20): """ 获取每日推荐歌单。 :param cookie: 你的网易云音乐登录Cookie字符串 :param limit: 想要获取的歌单数量 :return: 解析后的歌单列表,如果失败返回None """ url = 'https://music.163.com/weapi/v1/discovery/recommend/resource' # 构造请求参数 params_dict = create_request_params(limit=limit) 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, data=data, headers=headers, timeout=10) 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, limit=10) if playlists: for idx, pl in enumerate(playlists): print(f"{idx+1}. {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 model's maximum context length is...这个错误信息看起来像来自大型语言模型(LLM)API(如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, limit=1000, offset=0): 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应用逆向中常见的加密、签名和模拟登录问题。从实用角度看,它为你打开了一扇窗,让你能以编程的方式与喜爱的音乐平台交互,创造出属于自己的音乐工具。希望你在使用这些知识时,能始终秉持负责任和尊重的态度。