1. 项目概述用Python解锁你的Spotify音乐DNA最近在整理电脑时发现我的Spotify账号已经积累了7年的听歌记录。作为每天通勤必开音乐的人突然很好奇自己这些年到底听了些什么。官方提供的年度回顾虽然精美但数据维度有限。于是决定用Python写个脚本把那些沉睡在Spotify服务器上的原始数据真正榨出汁来。这个项目适合所有想了解自己音乐品味的数据爱好者。不需要专业编程基础只要会安装Python库就能跟着操作。最终我们能获得比官方报告更细致的分析比如每周听歌时段分布热力图常听歌曲的BPM每分钟节拍数变化曲线不同季节的流派偏好迁移歌手收听时长排行榜可能会发现一些自己都没意识到的隐形最爱提示Spotify官方API限制每小时最多300次请求建议选择3个月以上的数据时需要分时段获取2. 环境准备与API对接2.1 开发环境配置推荐使用VSCode Jupyter Notebook组合方便实时查看数据分析结果。需要安装的核心库包括pip install spotipy pandas matplotlib seaborn其中spotipy是Spotify官方API的Python封装库。如果遇到证书错误可以尝试pip install --upgrade certifi2.2 获取API凭证登录 Spotify开发者仪表盘创建新应用类型选个人研究用途记录下Client ID和Client Secret在设置中添加回调地址http://localhost:8888/callback建议将凭证保存在环境变量中import os os.environ[SPOTIPY_CLIENT_ID] 你的ClientID os.environ[SPOTIPY_CLIENT_SECRET] 你的ClientSecret os.environ[SPOTIPY_REDIRECT_URI] http://localhost:8888/callback2.3 认证流程实现使用OAuth2授权码流程获取tokenimport spotipy from spotipy.oauth2 import SpotifyOAuth scope user-library-read user-read-recently-played user-top-read sp spotipy.Spotify(auth_managerSpotifyOAuth(scopescope))首次运行会弹出浏览器窗口要求登录授权。成功后token会自动缓存到本地后续调用无需重复认证。3. 数据采集与清洗3.1 获取近期播放记录recent_tracks sp.current_user_recently_played(limit50)这个端点最多返回最近50条播放记录。如果需要更早的数据需要通过分页参数循环获取def get_all_recent_tracks(days30): results [] before int(time.time()) * 1000 while True: batch sp.current_user_recently_played(limit50, beforebefore) if not batch[items]: break oldest_time batch[items][-1][played_at] before int(pd.to_datetime(oldest_time).timestamp() * 1000) results.extend(batch[items]) if (pd.to_datetime(now) - pd.to_datetime(oldest_time)).days days: break return results3.2 获取曲目音频特征Spotify为每首歌提供了13种专业音频特征track_ids [item[track][id] for item in recent_tracks] audio_features sp.audio_features(track_ids)关键特征包括danceability舞蹈性energy能量感valence情绪积极度tempoBPMspeechiness口语化程度3.3 数据标准化处理将原始JSON转换为结构化DataFrameimport pandas as pd def flatten_track(track): return { played_at: track[played_at], name: track[track][name], artist: , .join([a[name] for a in track[track][artists]]), duration_ms: track[track][duration_ms], **track[track][audio_features] } df pd.DataFrame([flatten_track(t) for t in recent_tracks])处理时间字段df[played_at] pd.to_datetime(df[played_at]) df[hour] df[played_at].dt.hour df[day_of_week] df[played_at].dt.dayofweek4. 可视化分析与洞察挖掘4.1 听歌时段热力图import seaborn as sns import matplotlib.pyplot as plt plt.figure(figsize(12,6)) heatmap_data df.groupby([day_of_week,hour]).size().unstack() sns.heatmap(heatmap_data, cmapYlOrRd) plt.title(Weekly Listening Pattern) plt.xlabel(Hour of Day) plt.ylabel(Day of Week) plt.show()我的结果显示周三下午3-4点有个明显的收听高峰对应每周最困的工作时段。4.2 情绪能量矩阵plt.figure(figsize(10,8)) sns.scatterplot(datadf, xvalence, yenergy, hueartist, sizeduration_ms, sizes(50,200), alpha0.7) plt.title(Energy vs Valence by Artist) plt.legend(bbox_to_anchor(1.05, 1), locupper left)这个图能直观显示你常听歌曲的情绪分布。我发现自己工作时常听高能量低情绪值的电子乐而周末则偏好中能量高情绪值的独立摇滚。4.3 历史品味演变用滚动窗口分析音乐特征的变化df.set_index(played_at, inplaceTrue) weekly_features df[[danceability,energy,valence]].resample(W).mean() plt.figure(figsize(12,6)) for col in weekly_features.columns: sns.lineplot(dataweekly_features[col], labelcol) plt.title(Weekly Audio Feature Trends) plt.ylabel(Value)我的数据显示每年冬季valence值明显下降可能和季节性情绪波动有关。5. 高级分析与自动化5.1 创建个人音乐指纹用KMeans聚类识别你的音乐偏好模式from sklearn.cluster import KMeans features df[[danceability,energy,valence,tempo]] kmeans KMeans(n_clusters3).fit(features) df[cluster] kmeans.labels_ plt.figure(figsize(10,6)) sns.scatterplot(datadf, xvalence, yenergy, huecluster, paletteviridis)我的结果分出三类高能量工作音乐、中等能量休闲音乐、低能量助眠音乐。5.2 自动化周报生成用Jinja2模板自动生成HTML报告from jinja2 import Template template Template( h1Your Weekly Music Report/h1 pTop Artists: {{ top_artists|join(, ) }}/p !-- 更多模板内容 -- ) report template.render( top_artistsdf[artist].value_counts().head(3).index.tolist() )配合Windows任务计划或cronjob可以实现每周自动生成报告并邮件发送。6. 性能优化与注意事项请求限流处理from time import sleep from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1)) def safe_api_call(func, *args): try: return func(*args) except Exception as e: if 429 in str(e): sleep(int(e.headers.get(Retry-After, 10))) raise数据缓存策略import json from pathlib import Path CACHE_DIR Path(spotify_cache) def get_with_cache(endpoint, params): cache_file CACHE_DIR / f{endpoint}_{hash(frozenset(params.items()))}.json if cache_file.exists(): return json.loads(cache_file.read_text()) data safe_api_call(getattr(sp, endpoint), **params) cache_file.write_text(json.dumps(data)) return data内存优化技巧对于大型数据集使用dask替代pandas将datetime转换为unix时间戳存储对artist等字符串字段使用category类型我在处理3年数据时约2万条记录原始JSON文件有180MB经过优化后内存占用降至28MB。