cloudmusic
一个轻量的网易云音乐 Python 客户端:查询歌曲、专辑、歌单、歌词、评论和用户公开资料,并下载当前账号有权访问的完整音频。
支持 Python 3.10–3.14。保留 getMusic()、getPlaylist()、search() 等原有调用方式。
当前维护版本为 0.2.0。历史 HTML/PDF 文档对应 0.1.1,当前接口以本 README 为准。
安装
python -m pip install --upgrade cloudmusic
也可以从源码安装:
git clone https://github.com/p697/cloudmusic.git
cd cloudmusic
python -m pip install .
运行时仅依赖 requests 和 pycryptodomex,不再要求 lxml 或 threadpool。
快速开始
import cloudmusic
music = cloudmusic.getMusic(1347630432)
print(music.name, music.artist, music.album)
print(music.getLyrics()) # [原文歌词, 翻译歌词];缺失项为空字符串
results = cloudmusic.search("白日", number=5)
for song in results:
print(song.id, song.name)
playlist = cloudmusic.getPlaylist(310729011)
print(len(playlist))
歌曲信息与音频权限相互独立。获取歌曲名称、专辑、歌词或评论不需要先获取播放地址。访问 music.url、size、type、level 或 freeTrialInfo 时,才获取音频信息;同一批歌曲共享批量请求。音频信息缓存 60 秒,以减少重复请求和过期链接。
try:
print(music.url) # 没有播放权限时可能为 None;接口风控会抛出 APIError
print(music.level) # 服务实际返回的品质,可能低于请求品质
path = music.download(dirs="./downloads")
print(path) # 已保存文件的绝对路径
except cloudmusic.CloudMusicError as error:
print(error)
下载采用流式写入和临时文件替换。只有成功完成下载才覆盖同名目标文件;失败会保留已有文件并清理临时文件。仅有试听音频时抛出 UnavailableError,不会把试听片段当成完整歌曲保存。
配置会话
默认匿名访问。需要账号权限时,可以显式传入自己的 cookie;库不包含任何内置登录凭据,不自动读取浏览器或环境中的凭据。
import os
import cloudmusic
with cloudmusic.Client(
cookie=os.environ.get("NETEASE_COOKIE"),
timeout=(5, 20), # 连接超时、单次读取超时,单位秒
level="higher",
# proxies={"https": "http://127.0.0.1:7890"},
) as client:
music = client.getMusic(1347630432)
songs = client.search("白日", 5)
# 返回的 Music/User 对象保留配置;关闭 Client 后仍可按需调用。
print(music.getLyrics())
cookie 支持 cookie 字符串或字典。Client 复用查询连接并保留服务端更新的会话 cookie,使用完毕后应调用 close() 或用 with 管理。网络错误和风控错误不会自动反复重试;同批音频遇到验证/登录限制后,在 60 秒内复用该错误,避免批量任务重复触发限制。音频 CDN 请求不携带网易云登录 cookie。
该项目依赖网易云的非官方接口。接口返回内容受登录状态、隐私设置、地区和音频权限限制;升级客户端不能保证获取所有歌曲、无限评论或指定音质。不要将 cookie 提交到 Git 或输出到公开日志。
API
以下函数也可以通过 Client 调用:
| 函数 | 返回值 |
|---|---|
getMusic(id) |
一个 Music 对象 |
getMusic([id, ...]) |
Music 列表,保留输入顺序和重复 ID;单元素列表仍返回列表;空列表返回 [] |
getPlaylist(id) |
按歌单顺序排列的 Music 列表 |
getAlbum(id) |
按专辑顺序排列的 Music 列表 |
search(content, number=5) |
最多 number 个不重复的搜索结果;支持分页;无结果返回 [] |
getUser(id) |
User 对象 |
ID 支持正整数或整数字符串。无效参数抛出 ValueError;歌曲 ID 不存在时抛出 NotFoundError,其 ids 字段包含缺失 ID。歌单返回数量与服务报告的总数不一致时抛出 IncompleteResultError,不会静默当作完整歌单。
try:
songs = cloudmusic.getPlaylist(310729011)
except cloudmusic.IncompleteResultError as error:
print(error.actual_count, error.expected_count)
songs = error.partial_result # 调用方明确选择是否使用已获取部分
Music
元数据属性:id(字符串)、name、artist、artistId、album、albumId、picUrl。
按需获取的音频属性:url、size、type、level、freeTrialInfo。
| 方法 | 行为 |
|---|---|
download(dirs="", level=None) |
默认保存到当前目录的 cloudmusic 文件夹;level=None 沿用创建对象时的请求品质(默认 higher) |
getLyrics() |
返回 [原文, 翻译],缺失项为空字符串 |
getHotComments(number=15) |
最多 15 条热评 |
getComments(number) |
按时间从新到旧,使用游标分页;不足时返回可获取的数量 |
getCommentsCount() |
服务报告的评论总数 |
品质参数保留 standard、higher、exhigh、lossless。实际品质由接口和账号权限决定。指定音质不会丢失下载目录,文件扩展名取服务实际返回值。
评论字段:commentId、likeCount、content、time、userId、nickName、avatarUrl、vipType、userType。用户已注销或字段缺失时,昵称和头像可以为空。若服务表示还有数据但分页无法继续,抛出带 partial_result 的 IncompleteResultError,避免无限循环或重复结果。
User
属性:id、level、listenSongs、createTime、nickname(也支持 nickName)、avatarUrl、city、province、vipType、birthday、signature、fans、follows、eventCount、playlistCount、sex。部分资料可能因隐私设置缺失。
getPlaylist():分页获取可访问的用户歌单,返回信息字典列表。字段包括id、name、creatorId、playCount、createTime、coverImgUrl、updateTime、tags。getRecord(recordType=0):0 为所有时间,正整数为最近一周;返回{"score": ..., "music": Music}列表,只请求所需周期。
批量下载
loader = cloudmusic.createLoader(procs=2, dirs="./downloads")
loader.data = cloudmusic.getPlaylist(310729011)
try:
loader.start()
except cloudmusic.DownloadError:
print(loader.errors) # 失败项:以 data 中的索引为键
print(loader.results) # 成功保存的绝对路径,按输入顺序排列
最多同时使用 32 个工作线程。文件名会处理跨平台非法字符和过长名称;同名歌曲仍会覆盖同一目标文件,请选择适当的目录管理下载结果。
错误类型
均继承 CloudMusicError,无效调用参数使用标准 ValueError。
| 类型 | 含义 |
|---|---|
RequestError |
网络/超时/非 200 HTTP 响应;status_code 在 HTTP 失败时可用 |
APIError |
网易云业务错误;保留 code 和 message,包括风控、验证或登录要求 |
ResponseError |
JSON 或响应结构异常 |
NotFoundError |
指定歌曲不存在,保留 ids |
UnavailableError |
没有完整音频可供下载 |
IncompleteResultError |
歌单不完整或分页停止前进;保留 partial_result |
DownloadError |
文件下载未完整完成或批量下载部分失败 |
测试与开发
python -m pip install -e '.[test]'
python -m pytest --cov=cloudmusic --cov-branch --cov-report=term-missing
python -m ruff check cloudmusic tests
python -m build
默认测试禁止外部联网,并通过本地 HTTP 服务验证流式下载、错误响应、中断、临时文件清理和已有文件保护。CI 在 Linux、macOS、Windows 上运行 Python 3.10、3.12、3.14 的测试,以及独立 wheel 安装检查。
真实接口测试显式启用,依赖当前网络环境和服务状态,不参与必过 CI:
python -m pytest --live -m live -v
真实测试默认匿名,不修改账号内容,也不批量下载音频。远端拒绝会表现为测试失败,不会被记为成功。若已在本机配置测试账号的 NETEASE_COOKIE 环境变量,可以明确启用登录态测试:
python -m pytest --live --cookie-env NETEASE_COOKIE -m live -v --tb=short
未传 --cookie-env 时测试不会读取任何登录 cookie。具体实测范围和限制见 验收记录。
从 0.1.1 迁移
- Python 最低版本更新为 3.10。
- 原有高层函数和对象属性保留,修正了单元素列表返回类型。
- 音频属性按需读取;网络/权限错误可能在访问这些属性时出现。
- 下载默认沿用对象品质;明确传入
level="standard"时会请求该品质。 - 错误不再以字符串、打印信息、
None或意外KeyError混合返回;请处理相应异常。 query.py内部旧 HTML 抓取函数已移除。内部模块不是稳定 API。
发布维护版本
维护者在 GitHub Actions 手动运行 Publish to PyPI,选择 master 并填写与源码一致的版本号。流程先执行跨平台 CI,再构建、检查和独立安装分发包,最后通过 PyPI Trusted Publishing 上传;无需保存长期 API token。
PyPI 的可信发布者配置为仓库 p697/cloudmusic、工作流 release.yml、环境 pypi。发布环境仅允许 master 分支。修改版本号后应先通过 PR 合并,再启动发布。
历史贡献参考:PR #14、PR #17、PR #21。本轮吸收了跨平台路径和音质目录传递的修复方向;封面/标签写入功能暂未纳入。
MIT License · @p697
Metadata
Release files for cloudmusic 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cloudmusic-0.2.0.tar.gz | 34.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cloudmusic-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.6 kB
Release files / cloudmusic-0.2.0.tar.gz
| Download URL | cloudmusic-0.2.0.tar.gz |
|---|---|
| Size | 34.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c4ed5d7a40e2b7dda45ba8bca8a2ccff806cb6057bf8e7cc6918021939cfc7f6
|
|
BLAKE2b-256 checksum How to use checksums |
779c39859dbc84c6b386823afd7d0a8633e33783c4148a7ac4b6276524ccb770
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / cloudmusic-0.2.0-py3-none-any.whl
| Download URL | cloudmusic-0.2.0-py3-none-any.whl |
|---|---|
| Size | 21.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
396b233f60b98726e3f9fe94ecf91c96d50938fc243733bdc2ae9ed7021beb22
|
|
BLAKE2b-256 checksum How to use checksums |
6ef8e52e2e40b510cbb67830ae8d7d162ac30d177804fbc113c786f2493bab0d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency log