
简介本资源为基于Python的网易云音乐API设计与实现源码面向具备一定Python基础、希望学习接口设计与后端开发的开发者及音乐技术爱好者。项目以Python为核心结合HTML、JavaScript与CSS完整呈现从数据请求、处理到响应输出的API实现路径可用于二次开发或课程实践参考。压缩包共89个文件约134KB其中65个py脚本构成核心逻辑13个md文档提供说明与接口梳理另含html页面、css样式、js脚本及txt配置等辅助文件目录按功能模块划分结构清晰便于检索。目前已有376人学习下载。读者可从中获取一套可运行的API源码理解网易云音乐相关接口的模块组织方式、请求加密与响应处理思路并借助文档快速定位歌曲、歌单、评论、用户等业务模块的实现细节适合作为接口开发与项目结构学习的实践素材。1. 从一份 Python 网易云音乐 API 源码说起它到底能解决什么很多人第一次想拿网易云音乐的数据都是打开浏览器 F12 抓包看到一堆加密参数直接懵掉。weapi、eapi、linuxapi 三套接口AES 加密、RSA 加密、MD5 摘要混在一起请求体还是十六进制拼接手动拼一次能对换个接口就翻车。这份基于 Python 的网易云音乐 API 设计与实现源码干的就是把这一整套加密链路封装成可调用的函数让你用requests发一个普通 POST 就能拿到歌单、歌曲 URL、歌词、评论、搜索这些数据。它适合两类人一类是正在做课程设计、毕业设计需要一个完整可跑通的 Python 后端项目当参考另一类是写爬虫或做音乐管理工具不想每次都重新逆向加密逻辑直接拿现成的 API 层来用。源码本身是 Python 实现不依赖浏览器自动化纯 HTTP 请求跑起来轻量Linux 和 Windows 都能部署。核心价值不在于它有多少接口而在于它把加密参数生成、请求构造、响应解析这条链路拆得清楚你能看懂每一步在干什么而不是面对一个黑匣子。2. 加密参数怎么来的weapi 与 eapi 的请求构造逻辑2.1 两套接口体系的差异与选型网易云音乐的接口分几个版本最常用的是 weapi 和 eapi。weapi 走的是https://music.163.com/weapi/路径参数经过两次 AES 加密再拼接eapi 走https://interface.music.163.com/eapi/用的是 AES-ECB 加密加 MD5 摘要请求体格式也不一样。源码里一般会同时实现这两套因为不同接口只开放其中一种。比如获取歌曲播放地址weapi 的/weapi/song/enhance/player/url在部分场景下返回的 URL 可能为空需要换 eapi 的/eapi/song/enhance/player/url再试。选型上有个经验搜索、歌单详情、歌词这类接口用 weapi 就够了稳定且参数简单播放地址、评论分页这类对风控敏感的接口eapi 的成功率更高。源码里通常会把两套加密函数分开写调用时按接口类型切换。2.2 weapi 加密的 Python 实现weapi 的核心是两次 AES-CBC 加密。第一次用固定密钥0CoJUm6Qyw8W8jud加密明文得到中间密文第二次用随机生成的 16 位密钥加密中间密文。同时还要用 RSA 公钥加密这个随机密钥最终请求体里带params和encSecKey两个字段。import os import base64 import json from Crypto.Cipher import AES from Crypto.Util.Padding import pad # 固定密钥网易云 weapi 第一层加密用 MODULUS 00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7 PUB_KEY 010001 NONCE 0CoJUm6Qyw8W8jud def aes_encrypt(text, key): AES-CBC 加密key 为 16 位字符串 iv b0102030405060708 cipher AES.new(key.encode(), AES.MODE_CBC, iv) padded pad(text.encode(), AES.block_size) encrypted cipher.encrypt(padded) return base64.b64encode(encrypted).decode() def rsa_encrypt(text, pubkey, modulus): RSA 加密返回十六进制字符串 text text[::-1] rs pow(int(text.encode().hex(), 16), int(pubkey, 16), int(modulus, 16)) return format(rs, x).zfill(256) def weapi_encrypt(data): weapi 完整加密流程 text json.dumps(data) # 第一次加密用固定密钥 first aes_encrypt(text, NONCE) # 生成随机 16 位密钥 secret .join([chr(ord(a) i % 26) for i in range(16)]) # 第二次加密用随机密钥 params aes_encrypt(first, secret) # RSA 加密随机密钥 enc_sec_key rsa_encrypt(secret, PUB_KEY, MODULUS) return {params: params, encSecKey: enc_sec_key}这段代码里几个参数要特别注意。NONCE是固定的不能改改了服务端解不出来。iv也是固定的0102030405060708这是网易云前端写死的。secret随机密钥理论上可以任意 16 位字符串但源码里一般用字母表循环生成保证每次请求不同即可。rsa_encrypt里先反转字符串再转十六进制这是网易云特有的处理顺序错了加密结果就不对。2.3 eapi 加密的差异点eapi 的加密逻辑和 weapi 不同。它用的是 AES-ECB 模式密钥是e82ckenh8dichen8加密后直接转十六进制大写不需要 RSA 那一步。请求体格式是params十六进制字符串放在 POST body 里。from Crypto.Cipher import AES from Crypto.Util.Padding import pad EAPI_KEY e82ckenh8dichen8 def eapi_encrypt(url, data): eapi 加密url 参与摘要计算 text json.dumps(data) # 构造摘要消息 message fnobody{url}use{text}md5forencrypt digest hashlib.md5(message.encode()).hexdigest() # 拼接加密明文 plain f{url}-36cd479b6b5-{text}-36cd479b6b5-{digest} cipher AES.new(EAPI_KEY.encode(), AES.MODE_ECB) padded pad(plain.encode(), AES.block_size) encrypted cipher.encrypt(padded) return encrypted.hex().upper()这里36cd479b6b5是固定分隔符nobody和use、md5forencrypt也是固定拼接词。摘要计算时 URL 和请求体都要参与少一个服务端就返回错误。eapi 的返回结果有时是加密的需要再用同样的密钥解密源码里一般会封装一个eapi_decrypt函数处理响应。提示AES 加密的 padding 方式必须是 PKCS7Python 的Crypto.Util.Padding.pad默认就是不用手动改。如果用的是pycryptodome库导入路径是Crypto.Cipher.AES不是Cryptodome。3. 从请求到数据接口封装与响应解析的工程化处理3.1 请求会话与公共参数注入源码里不会每次请求都重新建连接通常用一个requests.Session保持会话同时把公共参数如csrf_token、cookie统一注入。网易云很多接口需要登录态匿名请求只能拿到部分数据。源码一般会预留cookie配置项你从浏览器登录后复制 cookie 填进去即可。import requests class NeteaseAPI: def __init__(self, cookieNone): self.session requests.Session() self.session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://music.163.com/, Content-Type: application/x-www-form-urlencoded, }) if cookie: self.session.headers[Cookie] cookie def request_weapi(self, url, data): 发送 weapi 请求 encrypted weapi_encrypt(data) resp self.session.post( fhttps://music.163.com/weapi{url}, dataencrypted, timeout10 ) return resp.json() def request_eapi(self, url, data): 发送 eapi 请求 params eapi_encrypt(url, data) resp self.session.post( https://interface.music.163.com/eapi url, data{params: params}, timeout10 ) return resp.json()timeout建议设 10 秒网易云接口偶尔会慢但不至于超过 10 秒。Referer必须带否则部分接口返回 403。Content-Type用表单格式不要用 JSON因为加密后的params是作为表单字段传的。3.2 常用接口的调用示例源码里一般会封装几个高频接口搜索、歌曲详情、播放地址、歌词、评论。以搜索和播放地址为例调用方式如下。api NeteaseAPI(cookie你的cookie) # 搜索歌曲 result api.request_weapi(/search/get/, { s: 海阔天空, type: 1, limit: 10, offset: 0 }) for song in result[result][songs]: print(song[id], song[name], song[ar][0][name]) # 获取播放地址eapi song_id 347230 url_data api.request_eapi(/song/enhance/player/url, { ids: f[{song_id}], br: 320000 }) print(url_data[data][0][url])搜索接口的type参数1 是单曲10 是专辑100 是歌手1000 是歌单。limit和offset控制分页offset是偏移量不是页码。播放地址接口的br是比特率常见值 128000、192000、320000但实际返回的 URL 码率取决于账号权限普通账号拿不到 320k 的链接。3.3 响应数据的清洗与字段映射网易云返回的 JSON 字段名比较随意比如歌手字段是ar不是artists专辑是al不是album。源码里一般会做一层字段映射把原始响应转成统一格式方便上层业务调用。def parse_song(raw): 把原始歌曲数据转成统一格式 return { id: raw[id], name: raw[name], artists: [a[name] for a in raw.get(ar, [])], album: raw.get(al, {}).get(name, ), duration: raw.get(dt, 0) // 1000, fee: raw.get(fee, 0), # 0 免费1 会员4 付费专辑 }fee字段很关键0 表示免费可播1 表示需要会员4 表示数字专辑需要购买。如果你做的是下载工具这个字段决定了能不能拿到真实播放地址。dt是毫秒转成秒更直观。注意评论接口分页有坑offset超过一定值后返回空列表不是接口坏了是服务端限制。常见做法是翻到空就停不要死循环。4. 避坑与排查跑这份源码时最容易翻车的五个地方4.1 加密库版本不兼容导致Crypto导入失败现象运行时报ModuleNotFoundError: No module named Crypto或者ImportError: cannot import name AES。原因Python 的加密库有两个常见包pycrypto和pycryptodome。pycrypto已经停止维护在 Python 3.10 以上装不上。很多人直接pip install crypto装的是一个无关的空包。解决卸载冲突包装pycryptodome。命令是pip uninstall crypto pycrypto -y然后pip install pycryptodome。装完后导入路径仍然是from Crypto.Cipher import AES不要改成Cryptodome。4.2 请求返回{code: 400}或参数错误现象接口返回code: 400提示params error或encrypt error。原因weapi 加密时secret随机密钥长度不对或者 RSA 加密时字符串反转步骤漏了。eapi 加密时 URL 和请求体拼接顺序错了或者 MD5 摘要没算对。解决先检查secret是不是严格 16 位再检查rsa_encrypt里有没有text[::-1]。eapi 的话把url、text、digest三部分打印出来对照url-36cd479b6b5-text-36cd479b6b5-digest这个格式逐字比对。分隔符是36cd479b6b5不是36cd479b6b。4.3 播放地址返回null现象调用播放地址接口data[0].url是null但歌曲信息正常。原因三种可能。一是歌曲需要会员fee字段是 1二是版权限制该歌曲在接口层面不对外放 URL三是br参数设太高账号权限不够。解决先看fee字段如果是 1 就换免费歌曲测试。如果fee是 0 还是 null把br降到 128000 再试。如果都不行换 eapi 接口重试weapi 和 eapi 的放行策略不完全一样。4.4 Cookie 过期导致登录态失效现象之前能拿到的数据过一段时间后返回code: 301或要求登录。原因网易云 cookie 有有效期一般几天到几周不等。源码里如果硬编码了 cookie过期后所有需要登录的接口都会失败。解决不要把 cookie 写死在代码里放到配置文件或环境变量。过期后重新从浏览器复制。常见做法是写一个check_login函数启动时先调一次用户信息接口返回code: 200才继续。4.5 高频请求触发风控现象连续请求几十次后接口开始返回code: 406或直接超时。原因网易云对同一 IP 的请求频率有阈值短时间大量请求会被临时限制。解决在请求之间加time.sleep(0.5)到1秒的间隔。如果是批量任务用队列控制并发数不要开几十个线程同时打。源码里一般会预留一个delay参数默认 0.5 秒批量场景调到 1 秒以上更稳。5. 进阶用法把 API 封装成可复用的命令行工具源码跑通之后下一步是把它变成一个随手能用的工具。我一般会在 API 层之上再包一层 CLI用argparse接收参数支持搜索、下载、导出歌单三种模式。这样不用每次开 Python 交互环境终端一行命令就能出结果。import argparse import json import os def cmd_search(api, keyword, limit): result api.request_weapi(/search/get/, { s: keyword, type: 1, limit: limit, offset: 0 }) for song in result[result][songs]: parsed parse_song(song) print(f{parsed[id]} {parsed[name]} {/.join(parsed[artists])}) def cmd_download(api, song_id, output_dir): data api.request_eapi(/song/enhance/player/url, { ids: f[{song_id}], br: 320000 }) url data[data][0][url] if not url: print(f歌曲 {song_id} 无可用播放地址) return resp api.session.get(url, timeout30) path os.path.join(output_dir, f{song_id}.mp3) with open(path, wb) as f: f.write(resp.content) print(f已保存到 {path}) if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(action, choices[search, download]) parser.add_argument(keyword) parser.add_argument(--limit, typeint, default10) parser.add_argument(--output, default./downloads) args parser.parse_args() api NeteaseAPI(cookieos.environ.get(NETEASE_COOKIE)) if args.action search: cmd_search(api, args.keyword, args.limit) elif args.action download: os.makedirs(args.output, exist_okTrue) cmd_download(api, args.keyword, args.output)这个 CLI 有几个设计点值得说。cookie 从环境变量读不写进代码避免泄露。下载时先判断url是否为空空就直接返回不要写空文件。timeout设 30 秒因为音频文件可能几 MB网络慢的时候 10 秒不够。输出目录用os.makedirs(exist_okTrue)重复运行不会报错。验证工具是否正常可以按这个顺序走一遍先python cli.py search 海阔天空看能不能列出歌曲再python cli.py download 347230 --output ./test看文件是否下载成功且能播放。如果搜索正常但下载失败大概率是 cookie 权限或歌曲fee字段的问题回到第 4 章排查。还有一个技巧把常用歌单的 ID 存到一个 JSON 文件里写一个export_playlist函数批量导出歌单内所有歌曲信息到 CSV。这样课程设计里做数据展示时直接读 CSV 就行不用每次调接口。歌单 ID 从分享链接里拿id后面的数字就是。从那以后我每次拿到新的 API 源码都强制先跑一遍「搜索 → 详情 → 播放地址」这条最小链路确认加密和 cookie 都没问题再往上叠业务逻辑。希望帮到你。本文还有配套的精品资源点击获取