1. 为什么分析Spotify听歌数据?
作为一个长期使用Spotify的音乐爱好者,我发现平台虽然提供了年度回顾功能,但远远不能满足我对个人听歌习惯的深度了解需求。通过Python分析自己的Spotify数据,可以解锁以下价值:
- 发现隐藏的音乐偏好:平台只会告诉你"最常听的歌手",但不会告诉你"每周三下午喜欢听什么风格"
- 量化音乐品味变化:用数据见证自己从流行乐转向爵士乐的审美变迁
- 创建智能播放列表:基于情绪、时间段等维度自动生成个性化歌单
- 音乐社交新玩法:生成可视化报告分享给朋友,比单纯晒歌单有趣得多
提示:Spotify官方API对个人数据获取非常友好,但需要先注册开发者账号。整个过程完全免费,且不会影响现有账号使用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与API接入
2.1 创建Spotify开发者应用
- 访问Spotify开发者仪表板
- 点击"Create App"按钮,填写基本信息(名称和描述随意)
- 记录下生成的
Client ID和Client Secret - 在设置中添加重定向URI(本地开发可用
http://localhost:8888/callback)
python复制# 配置示例
SPOTIFY_CLIENT_ID = '你的client_id'
SPOTIFY_CLIENT_SECRET = '你的client_secret'
REDIRECT_URI = 'http://localhost:8888/callback'
2.2 安装必要的Python库
推荐使用conda或pip安装以下核心库:
bash复制pip install spotipy pandas matplotlib seaborn
spotipy:Spotify官方API的Python封装库pandas:数据处理与分析matplotlib/seaborn:数据可视化
注意:如果遇到SSL证书错误,可能需要额外安装
certifi库并配置环境变量。
3. 获取个人听歌数据
3.1 认证流程实现
Spotify使用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=SPOTIFY_CLIENT_ID,
client_secret=SPOTIFY_CLIENT_SECRET,
redirect_uri=REDIRECT_URI,
scope=scope))
运行后会弹出浏览器窗口要求登录授权,完成后会自动跳转回调地址。
3.2 获取不同类型的数据
3.2.1 近期播放记录
python复制recently_played = sp.current_user_recently_played(limit=50)
返回数据包含:
- 播放时间戳
- 曲目信息(名称、艺人、专辑)
- 上下文信息(是否来自播放列表)
3.2.2 个人Top曲目
python复制top_tracks = sp.current_user_top_tracks(limit=50, time_range='medium_term')
time_range参数可选:
short_term(最近4周)medium_term(最近6个月)long_term(所有时间)
3.2.3 收藏的歌曲
python复制saved_tracks = []
results = sp.current_user_saved_tracks(limit=50)
saved_tracks.extend(results['items'])
while results['next']:
results = sp.next(results)
saved_tracks.extend(results['items'])
4. 数据清洗与结构化
4.1 构建DataFrame
将API返回的JSON数据转换为结构化表格:
python复制import pandas as pd
def parse_track(track_item):
track = track_item['track']
return {
'name': track['name'],
'artist': ', '.join([a['name'] for a in track['artists']]),
'album': track['album']['name'],
'duration_ms': track['duration_ms'],
'popularity': track['popularity'],
'explicit': track['explicit'],
'added_at': track_item.get('added_at', None)
}
tracks_data = [parse_track(item) for item in saved_tracks]
df = pd.DataFrame(tracks_data)
4.2 特征工程
添加衍生特征提升分析维度:
python复制# 转换毫秒为分钟
df['duration_min'] = df['duration_ms'] / 60000
# 提取年份信息
df['added_year'] = pd.to_datetime(df['added_at']).dt.year
# 艺人出现频率统计
artist_counts = df['artist'].value_counts()
df['artist_frequency'] = df['artist'].map(artist_counts)
5. 可视化分析实战
5.1 听歌时间分布分析
python复制import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(12, 6))
sns.histplot(data=df, x='duration_min', bins=30, kde=True)
plt.title('歌曲时长分布')
plt.xlabel('时长(分钟)')
plt.ylabel('数量')
plt.show()
5.2 艺人偏好雷达图
python复制from math import pi
# 获取Top5艺人
top_artists = df['artist'].value_counts().head(5).index.tolist()
# 准备雷达图数据
categories = ['歌曲数量', '平均时长', '平均流行度']
N = len(categories)
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]
plt.figure(figsize=(8, 8))
ax = plt.subplot(111, polar=True)
ax.set_theta_offset(pi / 2)
ax.set_theta_direction(-1)
for artist in top_artists:
artist_data = df[df['artist'] == artist]
values = [
len(artist_data),
artist_data['duration_min'].mean(),
artist_data['popularity'].mean()
]
values += values[:1]
ax.plot(angles, values, linewidth=1, linestyle='solid', label=artist)
ax.fill(angles, values, alpha=0.1)
plt.xticks(angles[:-1], categories)
plt.yticks(color="grey", size=7)
plt.legend(loc='upper right', bbox_to_anchor=(1.3, 1.1))
plt.title('Top艺人多维对比', size=11, y=1.1)
plt.show()
5.3 音乐品味演变时间线
python复制# 按年份统计特征
yearly_stats = df.groupby('added_year').agg({
'duration_min': 'mean',
'popularity': 'mean',
'name': 'count'
}).rename(columns={'name': 'song_count'})
# 双轴图
fig, ax1 = plt.subplots(figsize=(12, 6))
color = 'tab:red'
ax1.set_xlabel('年份')
ax1.set_ylabel('平均时长(分钟)', color=color)
ax1.plot(yearly_stats.index, yearly_stats['duration_min'], color=color, marker='o')
ax1.tick_params(axis='y', labelcolor=color)
ax2 = ax1.twinx()
color = 'tab:blue'
ax2.set_ylabel('平均流行度', color=color)
ax2.plot(yearly_stats.index, yearly_stats['popularity'], color=color, marker='s')
ax2.tick_params(axis='y', labelcolor=color)
plt.title('音乐品味演变趋势')
fig.tight_layout()
plt.show()
6. 高级分析技巧
6.1 音频特征深度分析
Spotify为每首歌曲提供了详细的音频特征:
python复制# 获取音频特征
track_ids = df.index.tolist()[:100] # 限制数量避免API限制
audio_features = sp.audio_features(track_ids)
# 转换为DataFrame
features_df = pd.DataFrame(audio_features).set_index('id')
features_df = features_df[['danceability', 'energy', 'valence', 'acousticness',
'instrumentalness', 'liveness', 'speechiness']]
这些指标包括:
- danceability:舞蹈性(0-1)
- energy:能量感(0-1)
- valence:情绪积极度(0-1)
- acousticness:原声程度(0-1)
6.2 聚类分析音乐类型
使用K-Means算法发现隐藏的音乐分组:
python复制from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
# 数据标准化
scaler = StandardScaler()
scaled_features = scaler.fit_transform(features_df.dropna())
# 肘部法则确定最佳K值
inertia = []
for k in range(1, 11):
kmeans = KMeans(n_clusters=k, random_state=42)
kmeans.fit(scaled_features)
inertia.append(kmeans.inertia_)
plt.plot(range(1, 11), inertia, marker='o')
plt.xlabel('聚类数量')
plt.ylabel('误差平方和')
plt.title('肘部法则')
plt.show()
6.3 创建智能播放列表
基于情绪分析自动生成播放列表:
python复制def create_mood_playlist(sp, mood, limit=20):
# 根据情绪筛选歌曲
if mood == 'happy':
condition = (features_df['valence'] > 0.7) & (features_df['energy'] > 0.6)
elif mood == 'calm':
condition = (features_df['energy'] < 0.4) & (features_df['acousticness'] > 0.6)
track_ids = features_df[condition].index.tolist()[:limit]
# 创建播放列表
user_id = sp.current_user()['id']
playlist = sp.user_playlist_create(
user=user_id,
name=f"我的{mood}歌单 - {pd.Timestamp.now().strftime('%Y-%m-%d')}",
public=False)
sp.playlist_add_items(playlist['id'], track_ids)
return playlist['external_urls']['spotify']
7. 项目优化与扩展
7.1 自动化数据更新
使用schedule库实现每日自动更新:
python复制import schedule
import time
def update_data():
# 获取新数据并合并到现有DataFrame
new_data = get_recent_tracks(sp)
global df
df = pd.concat([df, new_data]).drop_duplicates()
# 每天凌晨2点执行
schedule.every().day.at("02:00").do(update_data)
while True:
schedule.run_pending()
time.sleep(60)
7.2 部署为Web应用
使用Flask创建个人音乐仪表盘:
python复制from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def dashboard():
# 准备可视化数据
plots = {
'duration_dist': create_duration_plot(df),
'artist_radar': create_radar_plot(df)
}
return render_template('dashboard.html', plots=plots)
if __name__ == '__main__':
app.run(debug=True)
7.3 与Last.fm数据整合
增强数据集的方法:
python复制import pylast
# 配置Last.fm API
lastfm = pylast.LastFMNetwork(
api_key="YOUR_API_KEY",
api_secret="YOUR_API_SECRET")
# 获取歌曲的Last.fm标签
def get_track_tags(artist, title):
track = lastfm.get_track(artist, title)
return [tag.item.get_name() for tag in track.get_top_tags()]
8. 实际应用中的经验分享
在长期运行这个分析项目的过程中,我积累了一些宝贵经验:
-
API限制处理:
- Spotify API有严格的速率限制(每分钟约300次请求)
- 对于大批量数据获取,建议添加
time.sleep(0.5)避免触发限制 - 使用
try-except块捕获spotipy.SpotifyException异常
-
数据存储策略:
- 原始JSON响应建议保存到本地文件或数据库
- 使用
df.to_parquet()比csv节省50%以上空间 - 考虑使用SQLite轻量级数据库管理历史数据
-
可视化优化技巧:
- 使用
plt.style.use('seaborn')获得更好的默认样式 - 交互式可视化推荐Plotly或Altair
- 对于大型数据集,先用
df.sample(1000)下采样再绘图
- 使用
-
个人隐私保护:
- 敏感数据(如Client Secret)应存储在环境变量中
- 分享结果前过滤掉可能暴露个人习惯的信息
- 定期审查API权限范围,移除不必要的scope
-
性能提升方法:
- 对重复使用的数据添加内存缓存(如
@functools.lru_cache) - 使用多线程获取不同类别的数据
- 向量化操作替代循环(特别是pandas操作)
- 对重复使用的数据添加内存缓存(如
