1. 项目概述:用Python解锁你的音乐DNA
作为一名长期使用Spotify的音乐爱好者兼Python开发者,我最近发现了一个有趣的现象:虽然平台每年都会生成"年度回顾",但那些静态图表根本无法满足我对个人听歌习惯的深度挖掘需求。于是我用Python构建了一套完整的分析方案,结果发现了许多连自己都惊讶的听歌模式——比如我居然在每周三下午三点准时听古典音乐,而周末深夜则固定切换成电子音乐。
这个项目不需要任何特殊权限,只要你的Spotify账号能正常使用,就可以通过官方API获取完整的听歌历史记录。整个过程完全合规,数据仅保存在本地,既保护隐私又能获得比官方报告更深入的洞察。下面我将分享从数据获取到可视化分析的全套方案,包含多个你可能从未想过的分析维度。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与API配置
2.1 开发环境搭建
推荐使用Python 3.8+版本,主要依赖库包括:
spotipy:官方推荐的Python SDKpandas:数据处理核心工具matplotlib/seaborn:基础可视化plotly:交互式图表scikit-learn:进阶聚类分析
安装命令:
bash复制pip install spotipy pandas matplotlib seaborn plotly scikit-learn
注意:如果遇到SSL证书错误,可能需要额外安装证书包:
bash复制pip install certifi
2.2 Spotify开发者账号申请
- 登录Spotify开发者平台
- 创建新应用(类型选"个人使用")
- 记录下
Client ID和Client Secret - 在应用设置中添加重定向URI(本地开发可用
http://localhost:8888/callback)
2.3 认证流程实现
使用OAuth 2.0授权码流程获取访问令牌:
python复制import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = "user-library-read user-top-read user-read-recently-played"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
redirect_uri="http://localhost:8888/callback",
scope=scope))
首次运行时会自动打开浏览器完成授权,之后凭证会缓存在本地。
3. 数据采集与预处理
3.1 获取听歌历史记录
Spotify API提供了多种数据端点,最常用的是:
python复制# 获取最近50条播放记录
recent_tracks = sp.current_user_recently_played(limit=50)
# 获取短期(约4周)最常听的歌曲
top_tracks_short = sp.current_user_top_tracks(time_range='short_term', limit=50)
# 获取长期(数年)最常听的歌曲
top_tracks_long = sp.current_user_top_tracks(time_range='long_term', limit=50)
3.2 音频特征提取
每首歌曲都包含丰富的音频特征元数据:
python复制def get_audio_features(track_ids):
features = sp.audio_features(track_ids)
return pd.DataFrame(features)
# 示例获取特征
track_ids = [track['id'] for track in top_tracks_short['items']]
audio_features_df = get_audio_features(track_ids)
这些特征包括:
danceability(舞蹈性):0-1值,越高越适合跳舞energy(能量感):反映歌曲强度valence(愉悦度):音乐的情绪倾向tempo(速度):BPM值loudness(响度):以分贝为单位的整体响度acousticness(原声程度):原声乐器占比instrumentalness(器乐程度):人声含量
3.3 数据清洗与增强
原始数据需要经过以下处理:
- 时区转换:将UTC时间戳转为本地时间
- 特征标准化:不同量纲的特征需要归一化
- 空值处理:部分老歌曲可能缺少某些音频特征
- 时间特征提取:从播放时间中提取星期、小时等维度
python复制def enhance_data(df):
# 转换时间戳
df['played_at'] = pd.to_datetime(df['played_at'])
df['hour'] = df['played_at'].dt.hour
df['day_of_week'] = df['played_at'].dt.dayofweek
# 合并歌曲元数据
df['artist_name'] = df['track'].apply(lambda x: x['artists'][0]['name'])
df['track_name'] = df['track'].apply(lambda x: x['name'])
return df
4. 核心分析方法与实现
4.1 听歌时间模式分析
通过时间维度分析可以发现许多有趣的行为模式:
python复制import matplotlib.pyplot as plt
# 按小时分布的播放量
hourly_counts = df.groupby('hour').size()
plt.figure(figsize=(12,6))
hourly_counts.plot(kind='bar', color='#1DB954')
plt.title('播放次数的小时分布')
plt.xlabel('小时')
plt.ylabel('播放次数')
典型发现可能包括:
- 通勤时段的播放大高峰
- 工作时的背景音乐偏好
- 深夜的特定类型音乐选择
4.2 音乐偏好雷达图
综合音频特征可以生成个人音乐DNA画像:
python复制import plotly.express as px
# 计算各特征均值
features = ['danceability', 'energy', 'valence',
'acousticness', 'instrumentalness']
avg_features = audio_features_df[features].mean()
fig = px.line_polar(
r=avg_features.values,
theta=features,
line_close=True,
template='plotly_dark',
title='我的音乐特征雷达图')
fig.show()
4.3 音乐类型聚类分析
使用K-Means算法自动发现音乐分组:
python复制from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
# 选择特征列
X = audio_features_df[features]
# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 聚类(假设分为3类)
kmeans = KMeans(n_clusters=3, random_state=42)
audio_features_df['cluster'] = kmeans.fit_predict(X_scaled)
# 可视化
plt.scatter(
audio_features_df['valence'],
audio_features_df['energy'],
c=audio_features_df['cluster'],
cmap='viridis')
plt.xlabel('Valence')
plt.ylabel('Energy')
4.4 听歌情绪时间线
结合播放时间和歌曲情绪特征,可以绘制情绪变化曲线:
python复制# 按时间排序
df_sorted = df.sort_values('played_at')
# 计算滚动平均情绪值
df_sorted['rolling_valence'] = df_sorted['valence'].rolling(5).mean()
plt.figure(figsize=(15,5))
plt.plot(
df_sorted['played_at'],
df_sorted['rolling_valence'],
color='#FF6B6B',
linewidth=2)
plt.title('两周内的情绪变化趋势')
plt.ylabel('情绪值(滚动平均)')
5. 高级分析技巧
5.1 音乐探索广度指数
计算个人听歌的多样性程度:
python复制def calculate_diversity(df):
total_tracks = len(df)
unique_artists = df['artist_name'].nunique()
return (unique_artists / total_tracks) * 100
diversity_score = calculate_diversity(df)
print(f"你的音乐探索广度指数:{diversity_score:.1f}%")
专业提示:正常范围在30-70%之间,低于30%说明听歌范围较窄,高于70%则可能是"音乐探险家"
5.2 音乐新鲜度分析
比较新歌和老歌的播放比例:
python复制def get_track_age(track):
release_date = track['album']['release_date']
return (pd.to_datetime('today') - pd.to_datetime(release_date)).days
df['track_age'] = df['track'].apply(get_track_age)
# 分类统计
age_groups = pd.cut(
df['track_age'],
bins=[0, 365, 730, 3650, float('inf')],
labels=['1年内', '1-2年', '2-10年', '10年以上'])
age_dist = age_groups.value_counts(normalize=True) * 100
5.3 音乐记忆时间胶囊
找出特定时期的代表性歌曲:
python复制def time_capsule(start_date, end_date):
mask = (df['played_at'] >= start_date) & (df['played_at'] <= end_date)
period_df = df[mask]
# 找出播放次数最多的歌曲
top_tracks = period_df['track_name'].value_counts().head(5)
# 找出最具代表性的艺术家
top_artists = period_df['artist_name'].value_counts().head(3)
return top_tracks, top_artists
# 示例:分析去年夏天的音乐记忆
summer_tracks, summer_artists = time_capsule('2022-06-01', '2022-08-31')
6. 实战案例与可视化展示
6.1 个人音乐日历热图
python复制import seaborn as sns
# 创建小时×星期的播放量矩阵
heatmap_data = df.pivot_table(
index='hour',
columns='day_of_week',
values='track_name',
aggfunc='count',
fill_value=0)
plt.figure(figsize=(12,8))
sns.heatmap(
heatmap_data,
cmap='YlGnBu',
linewidths=.5)
plt.title('每周听歌模式热图')
plt.xlabel('星期')
plt.ylabel('小时')
6.2 音乐风格演变时间线
python复制# 按月分组计算特征均值
monthly_features = df.set_index('played_at').groupby(
pd.Grouper(freq='M'))[features].mean()
# 绘制多特征趋势图
fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(15,12))
for ax, feature in zip(axes.flatten(), features):
monthly_features[feature].plot(ax=ax, color='#4ECDC4')
ax.set_title(f'{feature}趋势')
plt.tight_layout()
6.3 音乐社交对比分析
如果有朋友也进行了类似分析,可以比较音乐品味:
python复制def compare_profiles(my_features, friend_features):
comparison = pd.DataFrame({
'You': my_features[features].mean(),
'Friend': friend_features[features].mean()})
comparison.plot(kind='bar', figsize=(12,6))
plt.title('音乐品味对比')
plt.ylabel('特征值')
plt.xticks(rotation=45)
7. 常见问题与解决方案
7.1 API调用限制处理
Spotify API有速率限制(每分钟约300次请求),解决方法:
python复制from time import sleep
def safe_api_call(callable, max_retries=3):
for attempt in range(max_retries):
try:
return callable()
except spotipy.exceptions.SpotifyException as e:
if e.http_status == 429: # Rate limit
retry_after = int(e.headers.get('Retry-After', 5))
sleep(retry_after)
continue
raise
raise Exception("Max retries exceeded")
7.2 数据不完整问题
如果发现历史记录不全,可能是因为:
- Spotify默认只保留最近50条播放记录
- 需要定期运行脚本收集数据(建议每周一次)
- 使用以下方法获取更多历史数据:
python复制def get_all_recent_tracks(sp, limit=50, max_items=1000):
results = []
after = None
while len(results) < max_items:
batch = sp.current_user_recently_played(
limit=limit,
after=after)
if not batch['items']:
break
results.extend(batch['items'])
after = batch['cursors']['after'] if batch['cursors'] else None
return results
7.3 音频特征解释困惑
常见音频特征的直观解释:
| 特征 | 低值表现 | 高值表现 |
|---|---|---|
| danceability | 古典乐、氛围音乐 | 迪斯科、流行舞曲 |
| energy | 摇篮曲、冥想音乐 | 金属乐、电子舞曲 |
| valence | 悲伤的民谣 | 欢快的流行乐 |
| acousticness | 电子合成音乐 | 不插电现场版 |
| instrumentalness | 说唱、人声流行 | 后摇、纯器乐 |
7.4 可视化优化技巧
让图表更专业的几个细节:
- 使用Spotify品牌色(#1DB954)
- 添加移动平均线展现趋势
- 对时间序列数据使用面积图增强视觉冲击
- 重要节点添加注释说明
python复制plt.style.use('ggplot') # 专业图表风格
colors = ['#1DB954', '#191414', '#FFFFFF'] # Spotify配色方案
8. 项目扩展方向
8.1 自动化日报系统
使用Python自动化工具定期生成听歌报告:
python复制import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def send_daily_report(df):
# 生成HTML内容
html_content = generate_html_report(df)
# 配置邮件
msg = MIMEMultipart()
msg['From'] = 'your_email@example.com'
msg['To'] = 'your_email@example.com'
msg['Subject'] = '你的每日音乐报告'
msg.attach(MIMEText(html_content, 'html'))
# 发送邮件
with smtplib.SMTP('smtp.example.com', 587) as server:
server.starttls()
server.login('username', 'password')
server.send_message(msg)
8.2 音乐推荐增强
基于分析结果改进Spotify推荐:
python复制def get_recommendations(sp, seed_tracks, target_features):
recommendations = sp.recommendations(
seed_tracks=seed_tracks,
target_danceability=target_features['danceability'],
target_energy=target_features['energy'],
limit=10)
return recommendations['tracks']
8.3 跨平台数据整合
结合Last.fm等其他音乐平台数据:
python复制import pylast
# Last.fm API配置
lastfm_network = pylast.LastFMNetwork(
api_key="YOUR_LASTFM_KEY",
api_secret="YOUR_LASTFM_SECRET")
# 获取Last.fm数据
lastfm_user = lastfm_network.get_user("your_username")
lastfm_tracks = lastfm_user.get_recent_tracks(limit=100)
8.4 实时听歌仪表盘
使用Dash或Streamlit构建交互式仪表盘:
python复制import streamlit as st
def build_dashboard(df):
st.title('我的Spotify分析仪表盘')
# 时间范围选择器
date_range = st.date_input('选择日期范围', [])
# 过滤数据
filtered_df = df[(df['played_at'] >= pd.to_datetime(date_range[0])) &
(df['played_at'] <= pd.to_datetime(date_range[1]))]
# 显示关键指标
col1, col2, col3 = st.columns(3)
col1.metric("总播放量", len(filtered_df))
col2.metric("独特艺术家数", filtered_df['artist_name'].nunique())
col3.metric("平均情绪值", filtered_df['valence'].mean().round(2))
# 显示热图
st.subheader('听歌时间分布')
heatmap_fig = create_heatmap(filtered_df)
st.pyplot(heatmap_fig)
9. 个人经验与避坑指南
在实际操作中,我总结了这些宝贵经验:
-
数据收集策略:Spotify的播放历史API最多只能获取最近50条记录,要建立定期(如每天)运行的自动化脚本,才能积累足够的历史数据。我使用Windows任务计划程序每天凌晨3点运行收集脚本。
-
音频特征陷阱:不是所有歌曲都有完整的音频特征数据,特别是某些老歌或小众歌曲。在分析前务必检查空值比例,我通常会过滤掉特征缺失超过30%的曲目。
-
时区问题:API返回的时间戳是UTC时间,需要根据本地时区转换。我曾经因为忽略这点,导致所有时间分析偏移了8小时(北京时间UTC+8)。
-
可视化优化:在绘制情绪变化曲线时,原始数据波动太大难以观察趋势。我发现使用7点移动平均(即每个点的前后各3个点取平均)能最好地平衡细节保留和趋势展现。
-
聚类分析技巧:音乐特征的量纲差异很大(如tempo是0-300,valence是0-1),必须进行标准化处理。我推荐使用RobustScaler而不是普通的StandardScaler,因为它对异常值更鲁棒。
-
API限流处理:当需要获取大量歌曲的音频特征时,很容易触发API速率限制。我的解决方案是将请求分批发送,每批50个ID,批次间暂停1秒。对于429错误,自动读取Retry-After头信息等待相应时间。
-
数据持久化:建议使用SQLite数据库存储历史记录,而不是每次都重新获取。我设计了增量更新机制,只获取上次收集之后的新播放记录。
-
隐私保护:虽然分析的是个人数据,但仍需注意安全。我的方案是完全在本地运行,敏感信息(如API密钥)存储在环境变量中,分析结果也仅保存在本地。
