用Python分析Spotify个人听歌数据:导出到可视化完整指南
发布时间:2026/10/1 17:56:42来源:尧图网络
很多人以为Spotify分析项目一定得先申请API密钥、配置应用、跟OAuth授权纠缠半天其实有一条更省事的路径直接在账户的隐私中心申请导出个人数据拿到压缩包后用Python几行代码就能开始分析。这个思路是我在折腾了整整两天API之后才回过味来的本文就把完整的实操流程、代码和踩坑点全部整理出来照着做就能快速摸清自己的听歌底细。这个项目适合所有有Spotify使用记录的Python学习者哪怕你刚会装pandas都能跟上。数据分析的尽头不是炫技而是更了解自己用自己真实的数据做练习比任何教科书案例都有趣得多。1. 项目拆解搞清楚要分析什么、怎么拿数据1.1 核心需求解析从看歌单到看行为单纯查看Spotify的播放记录客户端里已经能做了但客户端回答不了几个问题过去一年你到底听了多少个小时、哪些歌曲被你反复重放、周末和周三的听歌节奏差在哪、你究竟更爱用手机还是电脑听歌。这些问题全都藏在数据背后需要把播放历史下载下来用工具跑一遍。用Python分析的好处有几个一是pandas处理表格数据非常顺手二是Matplotlib、Seaborn作图生态成熟三是你可以一次性把多年记录全部加载进内存不需要像在手机端那样来回翻页面。整个项目的核心就是一次典型的本地数据分析流程从原始文件到清洗表格再到图表输出链路完整且能复用到其他场景。1.2 两条数据获取路径的对比与选择获取Spotify听歌数据主要有两条路这里对比一下各自的适用场景方便你按需选择。方案获取方式数据范围优点缺点官方数据导出账户隐私中心申请完整历史记录、设备信息、歌单快照无需复杂授权、数据天然离线导出请求需要等待处理Spotify Web API开发者后台创建应用按OAuth授权范围获取可自定义时间粒度、支持实时查询配置复杂、受限流约束我自己最终的落地方案是以官方导出文件为分析主数据源API只作为字段补充。对大多数想分析自己的历史数据的需求来说官方导出已经足够丰富。导出文件里包含的字段覆盖了播放时间、艺人、曲目、设备、平台、播放时长和是否跳过几乎覆盖了听歌行为分析的全部要素而且拿到的是历史全量数据这一点API反而做不到——API通常最多返回最近一年的播放记录而且无法分页拉取全部历史。2. 数据准备从申请导出到完成清洗2.1 在Spotify账户发起个人数据导出申请打开Spotify网页版进入账户设置找到隐私设置页面往下翻会看到下载您的数据的入口勾选需要的项目后点击提交即可。这里建议勾选全部可选项因为数据总量并不大我的完整导出压缩包不到10MB全部选上后续可玩性更高。提交之后不会立刻收到文件Spotify是通过邮件通知的形式发送下载链接处理时间没有固定标准快则几小时慢则几天。这个步骤完成之后可以先去做准备工作比如安装好Python环境等邮件到了直接开工。2.2 导出文件的目录结构与字段含义解压下载的压缩包后里面会有一批JSON文件加上一个说明文档。我这份文件里最有价值的两个文件是StreamingHistory0.json到StreamingHistory5.json的分片文件以及Identifiers.json。前者记录逐条播放行为后者记录艺人、曲目、专辑的标识码。来看一下StreamingHistory文件的单条记录长什么样{ endTime: 2024-05-18 08:32, artistName: 坂本龙一, trackName: Merry Christmas Mr. Lawrence, msPlayed: 291000 }四个字段的语义非常清晰endTime表示播放结束时刻msPlayed是这条记录实际播放的毫秒数。有一点要放在心里这份记录并不包含歌曲总长所以后面做跳过率分析时需要另外获取歌曲时长来推算完整播放的比例。2.3 用Pandas完成多文件加载与基础清洗推荐一个稳妥的加载方式用glob匹配所有StreamingHistory文件配合pandas循环读取后再拼接。我在第一次分析时直接用单个文件结果2023年之前的数据完全缺失因为Spotify会把记录横向切分成多个文件。import glob import pandas as pd files sorted(glob.glob(Spotify Account Data/StreamingHistory*.json)) frames [] for f in files: df pd.read_json(f) frames.append(df) history pd.concat(frames, ignore_indexTrue) print(history.shape) print(history.head())拿到合并后的表格后第一轮清洗要做这几件事检查缺失值、把endTime字符串转成时间类型、把msPlayed转成更直观的秒数或分钟数。清洗时最容易忽略的问题是endTime采用的是UTC时间还是本地时间这个问题单独拿出来说见我第5章的踩坑记录。history[endTime] pd.to_datetime(history[endTime]) history[played_seconds] history[msPlayed] / 1000 history[played_minutes] history[played_seconds] / 60清洗之后还应该加一个有效播放的判断口径因为Spotify会把极短时间的播放也写进记录里比如切歌时的不到一秒钟。建议把played_seconds 5的样本单独标记为valid_play为False后面的统计默认只计算有效播放否则一长串的秒级记录会把平均收听时长拉得奇低。3. 五个实用分析维度的代码实现3.1 最常听的艺术家与单曲排名排名统计是分析的第一步也是最有满足感的一步。按艺术家分组求和播放次数再排个序你的年度艺人就出来了。artist_stats history.groupby(artistName).agg( play_count(trackName, count), total_seconds(played_seconds, sum) ).sort_values(play_count, ascendingFalse) top_artists artist_stats.head(10) print(top_artists)这里我想强调一个细节分组统计时最好同时输出播放次数和累计秒数两个字段。因为有些艺人你虽然听了很多次但每首都只听半分钟另一些艺人你听得少但几乎都是整曲循环两者的热爱类型完全不同。把两个维度放进同一个表里能避免单一指标误导判断。单曲排名同理但建议以artistName trackName联合分组这样不同艺人的同名歌曲不会混在一起。track_stats history.groupby([artistName, trackName]).agg( play_count(endTime, count), total_minutes(played_minutes, sum) ).sort_values(play_count, ascendingFalse)3.2 收听总量的时间趋势分析把时间维度展开能看到自己听歌习惯的长期变化。先按月份聚合输出每个月的累计收听分钟数观察哪些月份是收听高峰哪些月份几乎音讯全无。这段逻辑对后面做年终总结式的可视化很有用。monthly history.set_index(endTime).resample(ME)[played_minutes].sum().reset_index()这里ME表示按自然月末聚合新版pandas建议用ME而不是M后者在按月聚合时会抛出FutureWarning。我把按月趋势和按年趋势都做了一份因为Spotify记录可能横跨多年只看月粒度时年份之间的对比不好发现。更进一步的玩法是把播放次数和播放时长两条曲线放在同一个时间轴上对比。播放次数高但时长平缓说明那段时间经常切歌反之说明听得比较专注。这种双轴图在Matplotlib里实现起来也不复杂。3.3 一天24小时的收听节律分析人的听歌行为有明显的昼夜节律早晨可能边通勤边听深夜可能独自戴耳机。把endTime里的小时字段提取出来按小时统计播放量就能画出一张我的一天声音曲线。history[hour] history[endTime].dt.hour hourly history.groupby(hour)[played_minutes].sum().reset_index()这个分析建议在工作日和周末分开看。我的数据呈现出明显的差异工作日有两个高峰分别是早上的通勤时段和晚上的加班时段周末的高峰则整体后移到下午和深夜。如果只看合并曲线这两个峰会被平均掉观察价值大打折扣。history[weekday] history[endTime].dt.dayofweek # 0周一, 6周日 history[is_weekend] history[weekday] 5 weekday_hourly history[~history[is_weekend]].groupby(hour)[played_minutes].sum() weekend_hourly history[history[is_weekend]].groupby(hour)[played_minutes].sum()3.4 设备与平台的偏好分析Spotify的导出数据里设备信息并不总在StreamingHistory里出现更多时候需要结合平台字段或者从Identifiers.json补齐。不过分片的StreamingHistory文件在部分版本的导出数据中会包含一个平台字段可以用来区分iOS、Android、Web Player、电视端等来源。如果取不到设备字段可以换个思路用播放时间段作为设备状态的代理比如通勤时段大概率是移动端深夜在家的时段大概率是桌面端或智能音箱。这种代理变量虽然不够精准但分析人群场景绰绰有余。3.5 跳过行为与完整播放率初探跳过率的分析有一个绕不开的坎只知道播放时长不知道曲目总时长就没法判断播完没有。两个应对方案里一个是接API逐首查询时长适合数据量小的情况另一个是我采用的工程化方案对高频播放的歌曲做一个时长映射表低频歌曲不再逐一比对。track_duration_map {} # 假设从API或Spotify的曲库数据中构建了该映射 history[track_total_ms] history[trackName].map(track_duration_map) history[completion_ratio] history[msPlayed] / history[track_total_ms]定义完整播放时需要留一个容差范围一般我设定为completion_ratio 0.9就算完整播放因为末尾的淡出阶段和手动切歌会导致播放记录不精确。验证下来用0.9作为阈值能比0.99避免很多边缘情况。4. 可视化让数据变成能讲故事的图表4.1 播放量TOP10横向条形图展示排名数据时横向条形图比纵向柱状图更容易阅读因为艺人和歌曲名通常是长文本纵向坐标轴会被压得完全看不清。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 中文字体处理 plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(10, 6)) top10 artist_stats.head(10).sort_values(play_count) ax.barh(top10.index, top10[play_count], color#1DB954) ax.set_title(播放次数最多的10位艺人) plt.tight_layout() plt.show()这段代码里最容易翻车的是中文字体设置。很多默认环境里SimHei并不存在输出图上会变成一个个方框。稳妥的做法是先看系统有哪些可用字体再做映射import matplotlib.font_manager as fm fonts [f.name for f in fm.fontManager.ttflist] print(fonts)4.2 按月的收听时长热力图月度分布用热力图展示比折线图更能捕捉多个维度的信息。把年份作为行、月份作为列单元格填充收听的分钟数一眼就能看出哪些时段有异常高值或静默期。这个图特别适合做年度回顾的封面素材。数据透视用pandas的pivot_table实现然后交给imshow渲染。如果想把热力图做得更细腻可以在每个格子上标注具体数值方便实际阅读而不是只看冷暖色差。4.3 艺术家占比的环形图当TOP艺人名单出来后大家习惯用饼图看占比但我更推荐环形图——中心区域留白可以额外放一个总收听从数之类的统计值信息密度更高。fig, ax plt.subplots(figsize(8, 8)) wedges, texts, autotexts ax.pie( artist_stats.head(5)[play_count], labelsartist_stats.head(5).index, autopct%1.1f%%, startangle90, counterclockFalse, wedgeprops{width: 0.4} ) ax.set_aspect(equal)一个小建议如果个别艺人占比超过40%饼图会非常失衡此时换成条形图反而更直观。可视化的核心是让人快速理解差异而不是制造视觉冲击。5. 常见问题与排查技巧实录5.1 导出的StreamingHistory为何有时间断档如果发现拼接后的数据里某几个月是空的先别怀疑数据丢了。在我的实际排查中最常见的原因是Spotify将长历史记录切成了多个文件而不同文件之间时间范围有重叠直接读取时没有覆盖到全部月份。也有一种情况是用户自己曾经使用过音乐平台导入导出工具部分记录没有被计入官方历史。遇到断档先做一个时间覆盖性检查print(history[endTime].min(), history[endTime].max()) print(history[endTime].dt.year.value_counts().sort_index())确认断档所在的具体年份后再去原始压缩包里翻一眼文件列表。我在某次分析中就发现有一个文件叫StreamingHistory_p1.json而不是数字编号被我的glob模式漏掉了导致整整8个月的数据没进分析。5.2 时区偏移怎么处理关于时区我踩过一次实实在在的坑。刚开始分析时我发现所有记录都比本地时间早了8小时导致凌晨1点的播放被记成了前一天的17点昼夜节律图完全变形。原因是导出文件的时间戳一律采用UTC而我的设备行为发生在中国标准时间。处理方式很简单加载后统一转换时区再取本地时间字段history[endTime] pd.to_datetime(history[endTime], utcTrue) history[endTime_local] history[endTime].dt.tz_convert(Asia/Shanghai) history[hour_local] history[endTime_local].dt.hour转换时区有一个连锁反应你画24小时节律图时必须使用转换后的小时字段任何基于原endTime的小时聚合都会得到同样的偏差结论。5.3 记录量过大时的性能优化思路如果听歌记录跨度好几年拼接后的DataFrame会达到几十万行。常规的分组聚合操作还能扛住但如果你做了逐行遍历去匹配曲目总时长运行时间会瞬间拉满甚至卡死。我踩过几次坑后总结出三个实用策略能用groupby就绝不用iterrows向量化操作速度提升几十倍不止导入时只用需要的字段usecols可以显著减少内存对超大数据集可以按年份切片分析最后再汇总。还有一个小技巧把artistName、trackName的字符串列转成category类型内存占用能进一步降低这一招在处理重复度高的艺人名时效果相当明显。5.4 关于隐私与代码复用的提示由于导出数据包含个人听歌行为建议不要把原始JSON文件传到公开仓库。做练习时可以将文件路径留空加入简单的虚拟数据生成函数既能保护隐私又方便其他朋友完整体验流程。我的仓库里就放了一个generate_sample_data.py用随机种子生成结构一致的假数据大家在完全相同的代码下也能跑通全流程只是图表内容变成了虚构数据。最后的几点心得折腾完这个项目我个人最大的体会是拿到数据之后第一版图不要追求复杂先画播放量TOP10和一个24小时分布图五分钟出图能快速建立起原来我的数据长这样的直观印象。很多人在开始就陷入数据清洗的泥潭反复调整格式和字段迟迟看不到成果热情很快就消耗完了。还有一个很小的技巧想分享给大家分析结果别只存在图表里把每个维度的结论汇总成一段文字比如我最喜欢周五晚上听歌最爱艺人是某位钢琴家平均每次播放约4分钟。这段文字比任何图表都更适合发到社交平台或者写进年终总结里。再往后扩展的话可以把这套分析思路迁移到网易云音乐或Apple Music的导出数据上整体框架不变只需要调整字段名的映射关系。也有些人会进一步训练一个简单的分类模型用播放行为和时段特征去预测心情状态那就属于音乐心理学的范畴了。总之这个项目可浅可深先从今天的代码开始跑起来吧。
网站建设高端定制企业官网