资讯动态

用Python和Spotify API分析你的听歌数据:从播放记录到音频特征可视化

发布时间:2026/9/17 14:30:11 来源:尧图企业网站定制
你有没有试过在年终总结的时候点开Spotify的年度回顾看着那几个“你最爱的艺人”“你循环最多的歌”发愣它给的榜单确实好看但我更在意的是那些它没告诉我的东西我到底一年听了多少小时的音乐我是不是总在星期一的深夜emo我的听歌口味真的和我自己以为的一样吗想要回答这些问题最直接的办法就是自己动手把数据拉出来做分析而Python加Spotify的官方API就是一套非常趁手的工具组合。这篇文章是我实际折腾了一整天的完整记录。我会从零开始讲清楚怎么注册Spotify开发者应用、怎么用Python拿到你账号的播放记录、再到怎么从时间、风格、音频特征几个维度拆解你的收听习惯最后会把数据画成图。整个过程完全基于Spotify官方API不需要爬虫不需要逆向协议干净合法。适合对Python数据分析刚入门、又恰好有Spotify账号的朋友也适合那些单纯好奇“大数据眼里的我到底在听什么”的人。如果你对音频特征、推荐算法、个人数据可视化这些话题感兴趣这篇的很多思路也可以直接迁移过去用。1. 项目拆解与方案选型1.1 这个项目到底能拿到哪些数据开始写代码之前先把底摸清楚。Spotify官方API针对用户数据开放了两个我这次会用到的核心接口一个是最近播放记录接口返回你过去一段时间听过的歌曲、艺人、专辑、播放时间点另一个是用户个人排行榜接口返回你的Top艺人和Top歌曲。前者能还原你的收听时间线后者能告诉你最钟爱的口味是什么。更进一步每首歌都能通过音频特征接口拿到一组描述曲目“物理属性”的数字比如跳舞性、能量值、情绪值、节奏速度这些结合起来就能做很多有趣的分析。需要注意一点Spotify API返回的数据是有保留时间窗口的。最近播放记录最多能往回看大概90天而且单次请求最多返回50条。如果你想分析自己一整年的收听轨迹光靠这个接口还不够必须定期采集并保存到本地。我这次分享的方案会同时覆盖“一次性拉取最近数据”和“用定时任务持续累积数据”两种思路你可以根据自己的需要选择。1.2 技术选型为什么是Spotipy而不是手写HTTP请求老实说Spotify的API本身设计得挺规整直接用requests手动调用也不是不行但里面最麻烦的是OAuth授权流程。你需要自己处理回调地址、token刷新、scope权限拼装这些代码写起来烦而且很容易在细节上出错。Spotipy这个第三方库把这一切都封装好了安装一条命令初始化之后就能直接调用接口代码量至少省掉一大半。我知道有些朋友对第三方库有顾虑担心封装太深出问题不好排查。但Spotipy在Python社区里是Spotify API事实上的标准客户端维护非常活跃文档也全用它的风险远低于自己手搓。我做这个项目的时候也对比过直接requests调API和用Spotipy的代码复杂度最终确定用Spotipy把精力放在数据和可视化上而不是跟鉴权机制较劲。1.3 你最终会得到一份什么样的输出我做完这套分析之后得到了这样几个东西一张按天和按小时统计的播放热力图能看出自己哪天听得最多、哪个时段是肾上腺素时刻一张艺人播放时长榜能看出到底哪个乐队真正占据了我耳朵最大的份额一张歌曲音频特征的雷达图能看出我最近偏好的歌曲在能量、舞动性、情绪这些维度上的平均值还有一份整理好的CSV文件把每一首歌、播放时间、艺人、音频特征都存了下来。整个过程不复杂但出来的结果确实让我重新审视了自己的听歌习惯原来我在深夜和洗澡时听的完全是两类风格。2. 环境准备与Spotify开发者配置2.1 Python环境与依赖安装这个项目对Python版本没有特殊要求3.8以上都没问题。如果你是从零开始我建议直接用官方安装包装最新稳定版安装的时候记得勾选“Add Python to PATH”。我自己用的是3.11跑这套代码完全没遇到兼容性问题。依赖方面只需要三个库Spotipy负责和API通信pandas负责数据处理matplotlib负责画图。如果还想做交互式图表可以额外装一个plotly。安装命令如下pip install spotipy pandas matplotlib国内用户如果下载速度慢可以在pip命令后面加-i https://pypi.tuna.tsinghua.edu.cn/simple指定镜像源。这三个库都很常见装完不需要额外配置什么。2.2 注册开发者应用拿到密钥想要调用Spotify API必须去开发者后台创建一个应用。打开Spotify Developer Dashboard登录你的账号点“Create App”填一个应用名字和描述然后会得到一个Client ID和Client Secret这两个字符串就是你的应用凭证相当于钥匙。创建应用的时候有个关键步骤很多人会忽略在应用设置里必须手动添加一个Redirect URI也就是回调地址。这是OAuth授权流程的一部分授权成功之后Spotify会把用户重定向到这个地址并在URL参数里附带一个授权码。开发阶段最简单的方式是填http://localhost:8888/callbackSpotipy会在本地起一个服务来接这个回调整个过程是全自动的。如果你没有填写这个地址后面授权的时候会直接报错。2.3 两种授权方式怎么选Spotify API的OAuth授权方式有好几种我这次用到的是Authorization Code模式也是获取用户私人数据唯一合法的模式。这种模式拿到的token带refresh能力有效期一个小时过期之后可以用refresh_token自动续期不需要用户重新授权非常适合定时采集数据。还有一种是Client Credentials模式只能访问公共数据比如专辑信息、艺人资料拿不到用户播放历史。我做了一个表格方便你区分模式能拿什么数据需要用户授权Token有效期适合场景Authorization Code用户播放记录、Top榜单、收藏需要1小时可刷新私人数据分析Client Credentials专辑、艺人、曲目信息不需要1小时不可刷新公共数据查询我这个项目必须用第一种。授权的时候Spotify会弹一个网页问用户是否同意把数据授权给这个应用点同意就行这个步骤本质上是告诉Spotify“这个应用可以读我的听歌数据”非常安全不会暴露密码。3. 数据获取实战从API拉取你的听歌记录3.1 初始化客户端处理跨平台的缓存问题代码的第一步是初始化Spotipy客户端把Client ID、Client Secret、Redirect URI这几个参数传进去同时声明需要哪些访问权限。Spotify的scope就是权限列表比如“读取最近播放记录”“读取Top榜单”你申请的scope越少用户越放心这是个好习惯。我在这一步踩过一个坑就是token缓存文件的权限问题。Spotipy默认会把token缓存信息写到当前用户目录下的配置文件里Windows和Linux的行为不太一样有时候会出现Permission denied的错误。我把缓存文件指定到项目目录下的本地文件就彻底规避了这个问题。初始化代码如下import spotipy from spotipy.oauth2 import SpotifyOAuth SPOTIPY_CLIENT_ID 你的Client ID SPOTIPY_CLIENT_SECRET 你的Client Secret SPOTIPY_REDIRECT_URI http://localhost:8888/callback scope user-read-recently-played user-top-read sp spotipy.Spotify(auth_managerSpotifyOAuth( client_idSPOTIPY_CLIENT_ID, client_secretSPOTIPY_CLIENT_SECRET, redirect_uriSPOTIPY_REDIRECT_URI, scopescope, cache_path.spotify_token_cache ))第一次运行这段代码终端里会提示你打开一个网址复制到浏览器授权然后Spotify会重定向到本地回调地址把授权码交给Spotipy之后一切就顺畅了。整个过程只需要手动操作一次后面刷新token都是自动的。3.2 拉取最近播放记录时区问题与去重逻辑最近播放记录接口是sp.current_user_recently_played()默认返回你最近24小时内的播放记录最多50条。但我想要的是过去90天的数据这需要对接口的before参数做分页每次取完一页就用最早一条的播放时间戳作为下一次请求的锚点往前翻页。这里有一个很坑的地方API返回的played_at是UTC标准时间如果你直接拿来当本地时间用会发现所有播放记录的时间都比真实时间早了8个小时左右你的深夜收听高峰期会平白无故跑到凌晨去。解决方法是用Python的zoneinfo模块把UTC时间转换到本地时区。还有一个很容易忽视的点最近播放记录在播放密集的时候会出现同一首歌短时间内反复被记录的情况比如你单曲循环一首歌三遍接口里就会有三条几乎相同的记录。做时间统计的时候这些数据是合理的但做“歌曲播放次数”统计时如果你只想算听了一遍还是一直在洗脑循环就得根据情况决定要不要去重。我通常保留所有记录因为它们确实代表了你真实的播放行为。核心代码大致是这样import pandas as pd from datetime import datetime, timedelta from zoneinfo import ZoneInfo def fetch_full_history(sp, days90): records [] before None end_time datetime.now(ZoneInfo(UTC)) - timedelta(daysdays) while True: batch sp.current_user_recently_played(limit50, beforebefore) if not batch[items]: break records.extend(batch[items]) oldest datetime.fromisoformat(records[-1][played_at].replace(Z, 00:00)) if oldest end_time: break before int(oldest.timestamp() * 1000) df pd.DataFrame([{ song: item[track][name], artist: , .join(a[name] for a in item[track][artists]), album: item[track][album][name], played_at_local: datetime.fromisoformat( item[played_at].replace(Z, 00:00) ).astimezone(ZoneInfo(Asia/Shanghai)), } for item in records]) return df这段代码跑完你会得到一个完整的DataFrame每一行就是你的一次播放行为。我实测下来一天不落连续听歌的人90天大概有几千条数据跑起来毫无压力。3.3 获取Top榜单和音频特征Top榜单接口比播放记录简单得多调用sp.current_user_top_tracks()和sp.current_user_top_artists()传入time_range参数就可以拿到不同时间范围的排名。time_range有三个值short_term约一个月、medium_term约六个月、long_term约一年。我建议三个都拉一份分别存起来因为对比不同时间范围的口味变化本身就是很有趣的分析方向。音频特征就更直接了把曲目ID批量传给sp.audio_features()每次最多传100个ID就能拿到每首歌的12项音频特征指标。这些指标包括跳舞性、能量、音量、语音密度、原声性、乐器性、现场感、情绪积极度、速度、调性等。拿到之后和歌曲信息合并到一个表里后面做特征分析就方便了。我这里贴一段拉取Top歌曲并补充音频特征的代码def get_top_tracks_with_features(sp, time_rangemedium_term): top sp.current_user_top_tracks(limit50, time_rangetime_range) tracks [item[id] for item in top[items]] features sp.audio_features(tracks) rows [] for track, feat in zip(top[items], features): if feat is None: continue rows.append({ song: track[name], artist: , .join(a[name] for a in track[artists]), danceability: feat[danceability], energy: feat[energy], valence: feat[valence], acousticness: feat[acousticness], tempo: feat[tempo], duration_ms: track[duration_ms], popularity: track[popularity], }) return pd.DataFrame(rows)如果你想把Top歌曲的完整歌词风格分类也可以顺手调用sp.artist()拿艺人的genres字段不过要注意不是所有艺人都填了风格标签有些小众艺人这个字段是空的。这个数据后面做风格统计会用到所以我一般是把Top艺人的信息也一起拉下来。4. 数据分析从时间、风格、音频特征三个维度看你的口味4.1 时间维度找出你的“标准收听时刻”数据拿到手后第一件事我建议先做时间维度的分析因为这是效果最直观、也是最能带来“啊原来我是这样的人”感觉的分析。把播放记录里的时间拆成小时、星期几、月份几个字段然后用pandas的groupby统计不同时间段的播放次数。我自己的结果很有意思工作日上午10点到11点出现一个小高峰对应我通勤和开始上班摸鱼放松的时间晚上21点到23点是绝对的主力时段而凌晨2点到5点播放量几乎为零说明我的睡眠习惯还算正常。按星期几来看周六的播放量比工作日高出一截看来我的耳朵周末比工作日忙多了。如果你的数据是跨了三个月的你甚至可以看到随着季节变化听歌口味的变化。这个分析还可以再做细一点比如按“小时星期几”生成一个7x24的热力矩阵能更精准地看出工作日和周末的收听结构差异。用pivot_table透视一下再扔给seaborn画热力图一张图就能装下所有信息。4.2 艺术家与风格谁才是你真正的本命把Top榜数据和播放记录里的artist字段聚合按播放次数排序能快速得出你最常听的艺人排名。这个排名和Spotify给你的年度报告有些微差异因为年度报告用的是官方算法可能考虑了跳过率、完播率这些因素而你自己的统计就是最朴素的出现次数各有各的参考价值。我观察了一下自己的艺人榜单发现第二名其实不是我主动去听最多的而是因为我常听的歌单里几乎每首都有他参与featuring。这就是数据的魅力它和你的主观感受对上了一部分又在某些地方给你出乎意料的反馈。风格维度需要一点额外处理。艺术家的genres字段如果没有我就用歌曲所在专辑的类型信息做近似。统计了一下我的风格分布之后我看清了一个事实我的播放列表里indie rock的占比远超我自己的预期而电子音乐反而没我记忆中那么重要。这种“数据纠正记忆”的体验挺奇妙的推荐每个做这个项目的朋友都感受一下。4.3 音频特征用数字描述歌曲的“性格”音频特征分析是这个项目里最有技术含量、也最好玩的部分。这12项特征是Spotify算法直接分析音频波形得出来的比如danceability分数越高越适合蹦迪energy越高听起来越“炸”valence越高听起来越阳光积极acousticness越高越像不插电的现场。我把自己最近听的Top 50首歌的音频特征算了个平均值发现我偏好的歌普遍是中等能量、中等跳舞性、情绪值偏低的这下算是在数据层面印证了我朋友说我“爱听丧歌”的评价。如果你想更认真一点可以把这些特征按不同的时间段分组对比比如工作日的歌和周末的歌在能量和跳舞性上有没有显著差异。我做过一次结果工作日听的能量值明显低于周末看来我的大脑比我诚实得多。这里还有一个值得做的事情用tempo也就是BPM来分析你的收听节奏。把歌曲的BPM画成直方图能看出你的收听偏好是偏快歌还是偏慢歌。我的分布峰值在110到130之间属于比较典型的流行和电子音乐区间同时也有一小撮慢歌集中在80左右对应那些我深夜听的Ballad类歌曲。5. 可视化把数据变成能发朋友圈的图5.1 用matplotlib快速出图数据分析得再深入如果最后只是印在终端里的一堆数字说服力总差那么点意思。所以可视化是必须的。我这次的主力绘图库是matplotlib虽然它默认样式丑了点但胜在功能强、能精确控制每个细节而且装好就能用不用额外折腾。先拿播放记录画一个“按小时播放次数”的柱状图代码不超过十行却能立刻看出你的收听习惯曲线。我做的时候顺便把x轴的标签旋转了一下不然0点到23点这些数字挤在一起根本看不清。这在解决坐标轴标签太密集的问题上是最常用的手段网上很多教程都没提这个细节。import matplotlib.pyplot as plt hour_counts df[played_at_local].dt.hour.value_counts().sort_index() plt.figure(figsize(12, 5)) plt.bar(hour_counts.index, hour_counts.values, color#1DB954) plt.xticks(range(24)) plt.xlabel(Hour of day) plt.ylabel(Play count) plt.title(My Listening Activity by Hour) plt.savefig(listening_by_hour.png, dpi150, bbox_inchestight) plt.show()如果想画得更高级一点可以用seaborn的热力图来做星期几和小时的二维分布视觉冲击力会强很多。我之前一直觉得seaborn和matplotlib是两套体系后来发现seaborn其实只是建立在matplotlib之上的高级接口用的还是同一套坐标系学了不亏。5.2 艺人Top榜和音频特征雷达图艺人的排名可以画成横向柱状图把最长的柱子放在最上面一眼就能看出谁是你的“音乐本命”。横向柱状图在艺人的名字特别长时比竖向柱状图友好得多不用旋转标签也不会重叠。音频特征雷达图是我觉得最适合发朋友圈的图。一个五边形或六边形的雷达图上把你的歌曲平均特征画成一条闭合曲线直观展示你在各个音质维度上的偏好偏向哪个方向。我用了matplotlib的极坐标功能来实现其实就是在极坐标系下画一个多边形然后填充颜色做出来的效果非常像那些音乐App里的“音乐人格测试”。5.3 保存数据给未来的自己留一份档案最后强烈建议把整理好的DataFrame保存成CSV文件。一方面Spotify的最近播放记录有90天窗口不保存就永远找不回来了另一方面存下来的数据是后续所有分析和可视化的基础后续想玩什么新花样直接读这个文件就行。df.to_csv(spotify_play_history.csv, indexFalse, encodingutf-8-sig)这里用utf-8-sig编码而不是默认的utf-8是有原因的Windows上的Excel打开CSV时默认按ANSI编码解析如果用普通UTF-8会有中文乱码问题。加一个BOM头就是utf-8-sig干的事Excel就能正确识别。这个细节我第一次写的时候没注意交付文件给朋友后被吐槽了才反应过来。6. 常见问题排查与避坑实录6.1 鉴权失败与权限不足跑代码最常见的报错是spotipy.exceptions.SpotifyException加上一堆401或403的HTTP状态码。401通常是Client ID或Client Secret写错了或者token过期没有被正确刷新403多半是scope权限问题比如你没有申请user-read-recently-played就去调用播放历史接口。还有一个比较隐蔽的原因是你在开发者后台把Redirect URI写错了或者在多个应用之间复制配置时把URI搞混了。我的建议是遇到权限相关的报错先把异常信息完整打出来看一遍Spotify的报错信息里通常会明确指出“仅授予了XXX权限需要XXX权限”照着改就行。6.2 请求频率限制Spotify API对请求频率有明确规定大概是每30秒最多请求一定次数的级别具体数字以官方文档为准。一旦超过会被返回429状态码表示请求太频繁了。这个问题的解决方案有两个一个是控制代码里的请求节奏在循环里加入time.sleep等待另一个是用官方的Retry-After头如果收到429就等它告诉你的秒数再继续。我在拉取音频特征的时候遇到过这个问题因为要处理的歌曲有几百首每次批量100个ID看起来不多但连续请求还是容易撞上限制。后来我加了一个简单的指数退避逻辑第一次失败等2秒第二次等4秒最多等30秒从那以后就再也没被限流过。6.3 时区和播放时间对不上这个坑我在前面提到过但值得再强调一次。played_at字段是UTC时间如果你不做时区转换分析出来的小时分布会整体偏移8个小时那些明明发生在晚上9点的播放会被显示成凌晨5点。我在代码里用ZoneInfo(Asia/Shanghai)做的转换你可以根据自己所在的时区来修改。6.4 接口返回的字段比想象中复杂有朋友可能第一次看到API响应会被吓到items数组里三层外三层全是嵌套的字典比如item[track][album][images][0][url]这种路径。直接用这样的嵌套结构做数据分析很容易晕我建议在第一步就把它拍平成表格只保留自己关心的字段。我的做法是先打印一条原始记录看看结构确认字段路径无误后再写解析函数避免盲猜。7. 进阶玩法让这套项目更值钱7.1 定时采集搭建个人音乐档案90天窗口是硬限制想突破就只能在数据产生的那一刻及时采集。我现在的做法是把采集脚本用系统自带的定时任务每天晚上跑一次把当天新增的播放记录追加到本地数据库里。这样坚持几个月后你会拥有一个完全属于自己的历史听歌数据库到时候做年度报告不用等Spotify发自己生成的还能更个性化。这应该是这个项目最有长期价值的延伸方向。7.2 结合排行榜和艺人风格做推荐分析当你攒了几千条历史数据之后可以做的事就更多了。比如用Top艺人的风格标签计算风格偏好权重然后去匹配你还没听过的同类艺人或者用音频特征数据做聚类分析找出你播放记录里潜在的几种“收听场景”每个聚类代表一类特定场景下的音乐偏好。我个人觉得再往深走一步这些特征完全可以喂给推荐模型给自己做一个私人推荐系统。7.3 把你的分析结果导出成网页报告matplotlib画出来的静态图虽然清晰但总觉得少了点互动性。如果你想做个能给别人点着玩的年度报告可以考虑把生成的结果整理成一份HTML文件或者直接用Streamlit做一个本地网页应用把图表、表格、统计摘要全都放进去。Streamlit的优势在于代码量极低拖一个横条就能筛选时间段。去年年底我做了一个给朋友玩的“听歌人格测试”页面用的数据和图表底层就是我这次分析的思路。8. 最后想说的几句实在话做数据分析这件事很多时候难的其实不是写代码而是从一堆毫无温度的数据里找到一个值得问的问题。我的体会是用Python分析Spotify听歌数据最大的收获不是那个漂亮的雷达图也不是那几张柱状图而是它让我换了一个角度去观察自己的日常。数据不会撒谎它会温柔地戳破你“我其实什么都听”的错觉然后告诉你你就是规律本身。如果你也想试试和自己的生活数据对话我建议你今天就申请一个Spotify开发者账号跑通第一个接口后面的事情会自然长出来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价