☰
用Python分析Spotify听歌记录:从API到可视化实战指南
2026/10/1 11:29:37 网站建设 项目流程

1. 内容整体设计与思路拆解

1.1 这个标题背后到底能做什么

先说结论:用Python分析Spotify的听歌记录,是所有数据爱好者最容易上手的练手项目之一。原因很简单——数据是现成的,接口是官方的,代码量不大,但能挖出的信息量却非常惊人。

我自己第一次跑通这个项目时,光是把三年多的流媒体播放记录装进程序,就发现了几件从来没注意过的事:某些凌晨两点还在循环播放的冷门歌,某位歌手突然在某个月占据了我80%以上的播放份额,还有一首我完全不记得的歌居然单曲循环过40多次。这些数据Spotify都悄悄记着,只是平时不会主动展示给你。

这个项目适合谁?三类人:一是想学Python但苦于没有实战数据的初学者,借这个项目能把pandas、request、可视化全家桶过一遍;二是深度音乐用户,想知道自己的收听习惯到底是什么样的;三是准备往数据方向转型的从业者,这比对着网上下载的电商订单假数据做分析要有意思得多,因为你对每一项产出都有切身的直觉去验证和纠错。

1.2 为什么选择Python来做这件事

选Python不是跟风,是真的合适。整个分析链路从数据拉取、清洗、计算到可视化,Python都有对应的成熟库,而且语法上对新手非常友好。如果换用Java或C++,请求API写起来要啰嗦好几倍;如果用R,可视化虽然好看但通用性差一些,处理API请求也不如Python顺手。

更重要的是生态。Spotify官方提供了Python专用的客户端库spotipy,底层封装了OAuth授权、token刷新、API请求这些繁琐流程。这意味着你不需要手写一整套HTTP授权逻辑,几十行代码就能拿到自己账号下所有可公开访问的数据。配合pandas做表格处理、matplotlib和seaborn做图表,整个分析闭环一气呵成。

在这套流程里,核心数据来源有三类:

  • Recently Played接口:获取最近50条播放记录,适合做实时行为分析。
  • Top Artists / Top Tracks接口:获取一段时间内的热门歌手和热门歌曲,支持3个月、6个月、长期三档。
  • 用户导出的扩展播放历史:Spotify允许用户在账号设置里申请下载全部历史数据,拿到的是一个包含多年播放记录的JSON文件,这才是做深度分析的重头戏。

三者互为补充,组合起来就是一个完整的分析体系。

1.3 两类数据源的取舍:快速验证与长期统计

我在实际项目中强烈建议两条路都走,但先后顺序要注意。先用Spotify API拿到最近播放记录做快速验证,确认授权、请求、解析基础链路没问题;再申请完整的数据导出,做长期趋势分析。

这两类数据最大的区别在于覆盖范围和时效性。API能拿到的数据非常有限,最近播放只有50条,Top列表也只是汇总结果,拿不到时间线。而导出数据是一个庞大的压缩包,里面包含从你注册Spotify起每一天、每一首歌、每一次播放、每一分钟的明细记录,包括播放时间、曲目名、艺人名、专辑名、当时使用的设备、播放结束时的进度秒数,甚至包括曲目的完整Spotify链接。

拿到这样的数据后,你几乎可以复现出任何主流的听歌报告。比如周杰伦在某个夏天的傍晚是不是被反复点播,通勤时段你更倾向于听播客还是音乐,新歌发布后你会在第几天开始循环它。这些问题的答案全部藏在时间戳和曲目ID的交叉组合里。

有一点要说清楚:Spotify的数据导出不是实时的,提交申请后可能需要几天时间才会收到邮件。官方说最多30天,实际我遇到的情况通常是1到2天。本着"把最不可控的环境问题提前解决"的原则,应该以提交数据导出申请为项目第一步,然后利用等待时间走API那套链路。

2. 核心细节解析与实操要点

2.1 环境准备:需要的Python库和版本考量

不论你是macOS、Windows还是Linux,Python版本建议3.9及以上。下面这几个库是本项目的核心依赖,建议统一安装最新版:

  • spotipy:Spotify官方API的Python SDK,负责OAuth授权流程和数据请求。
  • pandas:读入JSON、清洗数据、做数据透视和聚合运算。
  • numpy:数值计算。
  • matplotlib/seaborn:图表绘制与样式美化。
  • jupyter或ipython:推荐使用Jupyter Notebook做交互式探索,因为数据分析的调试过程需要频繁地分步查看中间结果。

安装命令一行搞定:

pip install spotipy pandas numpy matplotlib seaborn jupyter

如果你用的是Anaconda环境,考虑用conda install统一管理依赖,避免系统级Python环境被搞乱。这里有个小坑我踩过:spotipy对Python 3.8以下版本的支持有问题,安装后运行时会报dataclasses相关的导入错误,所以先确认python --version的结果再动手。

2.2 Spotify开发者账号注册与权限申请:最容易卡住的第一步

在写任何代码前,需要先到Spotify的开发者后台创建一个应用。这一步几乎每个人都会卡一下,因为流程里的术语不太直观。

操作路径是:登录Spotify网页版开发者后台,点击Dashboard,登录后点击"Create app"。填应用名称、描述、重定向URI这三个字段。Redirect URI对本地调试来说填http://localhost:8888/callback即可,这是spotipy库的默认回调地址,也可以填http://localhost:8080。填写普通的本机回环地址都是允许的。

创建完成后,应用页面会显示两串关键字符:Client ID和Client Secret。这俩就是你的API身份凭证。最后一步,记得在应用设置里的"Edit Settings"页面,把Redirect URI加入白名单,否则授权时会一直报错。

权限方面,不同的API端点需要不同的scope。分析个人听歌数据需要声明的scope主要有:

  • user-read-recently-played:读取最近播放记录。
  • user-top-read:读取热门歌手和歌曲。
  • user-read-playback-state和user-read-currently-playing:获取当前播放状态。

如果只是拿导出数据分析,其实不需要任何API权限,但为了完整流程,建议把上面几个scope都填上。授权时会看到一个页面提示"此应用将访问你的部分数据",点同意即可。这里强调一下,Spotify官方不显示"请求的scope列表"在你勾选时,代码里声明什么它就请求什么心里要有数。

2.3 数据导出的申请流程:最应该早早启动的环节

我常说,分析Spotify数据最影响体验的瓶颈不是代码而是等待。数据导出的申请入口在账号设置里:进入"Account",找到"Privacy settings",在"Download your data"一栏点"Request"。提交后会收到一封确认邮件。

这里有个细节:数据文件是按模块生成的,不一定一次给全。比如Extended streaming history是主分析素材,另外还有Search history、听歌付费账单模块等。使用的话,重点找名为StreamingHistory0.json(或Audio_Streaming_History系列)的文件,那种带数字序号的JSON才是播放明细。

有些用户可能申请多次,导致收到多个分片文件,比如StreamingHistory0.json、StreamingHistory1.json,内容是按时间切分的。处理时要把它们合并成一个DataFrame,别只看一个文件——我曾见过有人把第0号文件当成全部历史,结论错得离谱。

2.4 理解JSON结构:分析前的数据结构认知

拿到StreamingHistory文件后,第一件事是查看它的内部结构。这个文件是一个JSON数组,每条记录格式如下:

{ "endTime": "2024-05-21 15:32", "artistName": "坂本龙一", "trackName": "Merry Christmas Mr. Lawrence", "msPlayed": 312000 }

字段少得有点令人意外,但足够完成大部分分析。endTime是播放结束时间,不是开始时间,msPlayed是实际播放的毫秒数。要注意,这里的"播放量"既包括完整播完的曲子,也包括只听半截就切歌的记录。

真实数据中还会出现一个额外的坑:trackName字段偶尔会出现"单曲版"、"Live版"、"(Remastered)"这样的后缀,统计时如果不做归一化,同一首歌会被拆成好几个条目。后面我会给出具体的处理方案。

3. 实操过程与核心环节实现

3.1 通过Spotify API获取最近播放记录

先放上一段可以直接运行的脚本框架。这段代码负责授权并拉取最近50条播放记录:

import spotipy from spotipy.oauth2 import SpotifyOAuth CLIENT_ID = "你的Client ID" CLIENT_SECRET = "你的Client Secret" REDIRECT_URI = "http://localhost:8888/callback" sp = spotipy.Spotify(auth_manager=SpotifyOAuth( client_id=CLIENT_ID, client_secret=CLIENT_SECRET, redirect_uri=REDIRECT_URI, scope="user-read-recently-played user-top-read", cache_handler=spotipy.cache_handler.CacheFileHandler( username="你的用户名" ) )) recent = sp.current_user_recently_played(limit=50) for item in recent["items"]: track = item["track"] played_at = item["played_at"] print(f"{played_at} | {track['artists'][0]['name']} | {track['name']}")

运行后,浏览器会自动弹出一个授权页面,登录Spotify账号,同意授权后会跳转到本地回调地址,然后命令行窗口就会输出最近播放的歌曲清单。

这个过程中需要注意的细节是缓存。spotipy会把token缓存到本地文件.cache-你的用户名,缓存有效期内不会重复弹出授权页面。如果授权信息过期想重新授权,删除缓存文件再运行一次即可。

3.2 读取并合并历史导出文件:从JSON到DataFrame

接下来处理数据导出的大文件。一个多年的播放记录文件往往有几万到几十万行,直接用Excel打开会卡死,pandas处理则很轻松。

import pandas as pd import glob files = glob.glob("StreamingHistory*.json") dfs = [pd.read_json(f) for f in files] df = pd.concat(dfs, ignore_index=True) print(f"总记录数: {len(df):,}") print(df.head())

如果文件路径里有中文,Windows平台偶尔会报编码错误,统一用encoding="utf-8"阅读。pd.read_json对标准JSON的解析能力足够健壮,不用手动处理嵌套结构。

此时做一个初始清洗:msPlayed是播放时长,可以换算成分钟列;endTime是字符串,要转成datetime类型。

df["endTime"] = pd.to_datetime(df["endTime"]) df["minutes"] = df["msPlayed"] / 60000.0

由此我们可以开始第一层统计。比如算出总收听时长、最常听的歌手、最常听的曲目、一天中哪个时段听得最多。这些都是投入产出比最高的分析点。

3.3 解析播放状态API:写一个"当前正在收听"的实时面板

除了历史数据,我们还可以利用API实现在线状态查询。例如写一个简短的Python脚本,每隔10秒刷新一次当前播放状态,输出正在播放的歌名和艺人:

import time while True: current = sp.current_user_playing_track() if current and current["is_playing"]: track = current["item"] progress = current["progress_ms"] / 60000 duration = track["duration_ms"] / 60000 print(f"正在播放: {track['name']} - {track['artists'][0]['name']} " f"({progress:.1f}/{duration:.1f}分钟)") else: print("当前没有在播放音乐") time.sleep(10)

这个脚本本质是一个"监听器",但它很依赖你在真实设备上的播放状态。如果你用的不是Spotify,而是别的播放器,这个功能就无法工作。这个限制在文档里不会明说,但在实际应用中很容易踩到。

3.4 深入分析维度:从聚合统计到时间序列

有了干净的数据,就可以做几个真正有洞察力的分析。

收听集中度分析。计算每个歌手的播放占比,并用累计占比画出帕累托图。比如我自己的数据呈现出典型的二八定律:不到10%的歌手贡献了超过60%的播放时长。

artist_stats = df.groupby("artistName")["msPlayed"].sum().sort_values(ascending=False) artist_stats["cum_percentage"] = artist_stats.cumsum() / artist_stats.sum() * 100 print(artist_stats.head(20))

时段活跃分析。按小时的周期做热力图,能直观看出自己是早上通勤听得久,还是睡前听得久。把一天24小时和星期几两个维度组合起来。

df["hour"] = df["endTime"].dt.hour df["weekday"] = df["endTime"].dt.dayofweek # 0=周一 heatmap_data = df.pivot_table(index="weekday", columns="hour", values="msPlayed", aggfunc="sum", fill_value=0)

这里用pivot_table比groupby更直观,它直接返回一个宽表格式,拿来画热力图几乎不需要再加工。有一点小提醒:这里的endTime是播放结束时间,凌晨0点前后的数据会归属到当天末尾,可能导致边界小时的数据波动,分析时要留意。

年度对比分析。导出数据的时间跨度覆盖多年,按年份汇总就能看出自己的音乐口味变化轨迹。比如各年份主听歌手的更替、播放总量的升降。

3.5 可视化:把数据变成能讲故事的图表

可视化的核心不是炫技,而是让规律肉眼可见。推荐三张图组合使用。

第一张是歌手总播放时长条形图。取Top 15歌手,用seaborn的barplot画出水平条形图,横轴卡播放分钟数。顺序按从高到低排列,观察分布形状。

import seaborn as sns import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False top_artists = df.groupby("artistName")["minutes"].sum().nlargest(15).sort_values() plt.figure(figsize=(10, 8)) sns.barplot(x=top_artists.values, y=top_artists.index, hue=top_artists.index, palette="viridis", legend=False) plt.xlabel("累计播放时长(分钟)") plt.title("最常听的Top 15歌手") plt.tight_layout() plt.show()

中文字体在Linux服务器上经常显示成方框,需要在系统里安装中文字体包。macOS上改["Arial Unicode MS"]即可,Windows上用SimHei。

第二张是每日播放量时间序列图。横轴是日期,纵轴是每天播放总时长。这个图能一眼识别出间歇性"爆发式听歌"的日期,通常是换工作、搬家、考试周前后。对时间序列顺手做一次滚动平均(比如7天窗口),更能看出长期趋势。

第三张是一天24小时的播放热度热力图。按星期几和小时组合,颜色越深代表播放总时长越高。这几乎是所有朋友看到都想问"怎么做到"的一张图,因为它新颖且有真实数据支撑。

可视化的经验是:先把统计结果跑出来,再挑最想讲的故事配图。不要一开始就追求花哨,柱状图和折线图已经能覆盖90%的需求。

4. 常见问题与排查技巧实录

以下是实际使用中反复出现的问题,整理成速查表,能省掉大量排查时间。

问题现象可能原因排查与解决办法
授权页面一直报redirect_uri_mismatch回调地址没有加入白名单到开发者后台把实际使用的URI完整填入白名单
请求时报No token providedtoken缓存过期或未保存删除.cache-用户名文件后重新运行
返回的播放记录只有50条,且重复Recently Played接口本身就是有限拉取改用数据导出文件做长期分析
中文歌手名显示为方块matplotlib缺少中文字体设置plt.rcParams["font.sans-serif"]为系统已装中文字体
msPlayed为0的记录很多歌曲刚播放几秒就被切走,或缓存未刷新默认msPlayed < 30000(30秒)的记录可视为跳过,分析时酌情过滤
JSON文件读取报错Expecting value文件损坏或编码异常用二进制方式打开,检查文件头部是否含BOM
多个StreamingHistory文件时间有重叠不同导出批次的数据范围重叠用drop_duplicates(subset=["endTime", "trackName", "artistName"])去重

4.1 过滤无效数据:到底要不要删掉短播放记录

这是很多人纠结的问题。播放不足10秒的歌,严格来说没有"消费"意义,统计时会拉低平均播放时长,也会让"最常听歌曲"的排序失真。

我的处理方案是:

valid_df = df[df["msPlayed"] >= 10000].copy()

保留10秒以上的记录。如果做的是"跳过率"分析——即统计每次播放数据里短播放的比例,那保留全部数据反而有用。也就是说,过滤策略取决于你想回答什么问题,没有绝对标准答案。这里我建议在初版分析中直接用全量数据,先感受原始分布,再决定是否清洗。

4.2 时区问题:为什么我的歌曲归到了错误的日期

endTime字段的时区是UTC还是本地时间,这个事儿官方文档写得不清楚,只有实际操作才能确认。就我的数据来看,字符串里不带时区信息,读取为本地时间即可。但要小心:如果你的电脑系统时区不是本地的,pd.to_datetime解析出来后的时辰会对不上。

保险做法是显式指定时区:

df["endTime"] = pd.to_datetime(df["endTime"]).dt.tz_localize("UTC").dt.tz_convert("Asia/Shanghai")

其实这个操作用在带时区信息的数据上是必须的,对不带时区的数据要小心,强行设置可能导致双倍偏移。务实一点的建议:先跑一下df["hour"]的分布,看看是否和你真实的作息峰值吻合,如果不吻合再检查时区设置。

4.3 刷播放数据导致的异常值处理

不管是否直接相关,说一个真实现象。只要Spotify账号播放过快、大量跳过,服务器端可能会把某些播放记录记成0毫秒。这类脏数据会让整体播放时长缩水。可以按设备维度分组查看,某些设备(比如车载音响)经常出现断连导致播放时长波动大。把明显异常的"1秒播放+结束"记录打上标记做删除或单独统计,都是合理操作。

4.4 性能优化:几十万条数据怎么跑得快

当数据量超过10万条,pandas的groupby仍然很快,但apply如果写不好会卡很长时间。几个实际优化习惯:

  • 用category类型压缩低基数字段,比如artistName和trackName。
  • 尽量用向量化运算替代逐行iterrows。
  • 聚合测试时先sample(10000)跑通逻辑,再全量运行。
  • 如果多次实验,把清洗好的中间结果存成parquet或pickle,避免重复解析JSON。
df.to_parquet("clean_data.parquet")

下次就pd.read_parquet("clean_data.parquet")直接加载,速度能提升近一个数量级。

5. 拓展玩法:分析还能往上走多深

基础的分析链路跑通后,这个项目的进阶空间其实很深。我把自己试过的几个方向梳理一下,你可以从中挑选感兴趣的去扩展。

5.1 曲目特征分析:为什么你会喜欢这些歌

Spotify有个很强大的功能,就是每首歌都有12个音频特征指标:能量、舞蹈性、声学度、器乐度、语速、音量等,全部可以通过API拿到。假如你把你Top 100歌曲的音频特征全部拉下来,做一次聚类分析,得到的簇大概率对应几种稳定的音乐场景:健身房燃脂、深夜emo、通勤轻音乐。

track_ids = [item["id"] for item in top_tracks] features = sp.audio_features(track_ids)

然后合成矩阵,用KMeans聚类。由于这类API在线拉取有速率限制,批量运行时最好加time.sleep(0.1)做限速。这个扩展项目能回答一个很有情绪价值的问题:"我的音乐品味到底是什么样的"。

5.2 歌词文本分析:从歌词里读出你的情绪画像

借助歌词平台的API下载自己所爱歌曲的歌词,然后做分词、词频统计和情感分析,形成一个"最喜欢的100首歌里出现最多的关键词"榜单。这类分析所用到的中文分词库是jieba,情感倾向可以用SnowNLP来做粗粒度判断。虽然准确性不能跟商业级情感分析系统比,但作为个人娱乐项目完全够用。

做的时候有个坑:很多歌词含有英文,中英混合的分词要做过滤处理,停用词表要自定义,否则高频词全是"you"、"the"、"的"这类无意义的词。

5.3 相互推荐:用协同过滤的思路找"另一个我"

如果你能拿到几个朋友的播放记录,可以算一下歌手重叠度。社交关系题在数据分析里向来能引发猛烈讨论。挑两位朋友的歌手占比向量,计算余弦相似度或Jaccard系数,结果往往让人会心一笑:你觉得品味差不多的人,数据上可能差得很远。

5.4 把分析工具化:做一个每周自动报告

更进一步,可以用cron或计划任务,每周固定时间运行脚本,自动拉取最近一周的播放记录,把统计结果渲染成HTML报告并邮件发送。这算是这个项目的终极形态——从一次性分析进化成持续在跑的数据服务。实现上只需要在脚本入口增加一个定时触发逻辑,并做好日志输出即可。

我自己跑了几周后发现,每周报告最有价值的功能不是统计,而是"变化检测":这周有没有突然冒出一个从来没有听过的歌手,有没有连续三天播放同一张专辑,这些都是值得回答的有趣问题。

实操总结与经验收尾

回头看我做这个项目的整个历程,最深的体会就是:数据本身不会讲故事,但如果你足够了解自己,数据会不断验证或推翻你的直觉。说句掏心窝的话,请一定先跑通API快速那条链路再等导出文件,不要让等待时间拖垮了你的兴趣。导出数据里的时间戳和艺术家名字,其实已经足够完成文章里提到的所有统计;音频特征再分析则是锦上添花,可以后续再补。

另外一个建议是:保留原始下载的压缩包,只读不解压,程序里始终从原始文件解析。这样每次调整清洗逻辑时,都能从一个稳定的初始状态开始,避免因为重复处理导致数据污染。

这个项目的好玩程度完全取决于你投入了多少曲目、经历了多少播放场景。如果手头有一份Spotify数据,我建议立刻动手跑起来——分析结果里大概率有让你"哎呀,居然会这样"的瞬间。那些瞬间,才是数字真正有了温度的证据。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询