1. 为什么需要分析Spotify听歌数据?
作为一名长期使用Spotify的音乐爱好者,我发现自己经常陷入这样的困惑:明明收藏了几千首歌,却总是反复听那几十首;明明觉得自己音乐品味很广,但年度回顾时才发现原来自己这么"专一"。这促使我开始思考——我们真的了解自己的听歌习惯吗?
Spotify作为全球最大的音乐流媒体平台,每天产生海量的用户行为数据。这些数据就像一座金矿,记录着我们的音乐偏好、情绪变化甚至生活习惯。通过Python分析这些数据,我们可以:
- 发现隐藏的音乐偏好模式(比如特定时间段、季节或情绪状态下的听歌倾向)
- 量化自己的音乐探索行为(新歌尝试率、流派多样性等)
- 识别听歌习惯中的"舒适区"和潜在偏见
- 为音乐推荐系统提供更精准的反馈
- 创建个性化的音乐统计数据可视化
提示:Spotify的API设计非常友好,即使没有专业数据分析背景,通过Python也能轻松获取和处理这些数据。我在2020年开始这个项目时,只用了不到50行代码就完成了基础的数据采集和分析。
2. 准备工作与环境配置
2.1 获取Spotify API访问权限
首先需要在Spotify开发者仪表板创建应用:
- 登录Spotify开发者账号(与普通账号相同)
- 点击"创建应用",填写基本信息(名称和描述随意)
- 记下生成的Client ID和Client Secret
- 在设置中添加重定向URI(本地开发可用http://localhost:8888/callback)
注意:Client Secret相当于密码,绝对不能直接写在代码或公开仓库中。我通常使用环境变量或配置文件管理,后面会具体说明。
2.2 Python环境准备
推荐使用Python 3.8+版本,主要需要以下库:
bash复制pip install spotipy pandas matplotlib seaborn python-dotenv
- spotipy:官方推荐的Spotify API Python客户端库
- pandas:数据处理和分析
- matplotlib/seaborn:数据可视化
- python-dotenv:管理环境变量
我习惯用VS Code开发,配置了Python扩展和Jupyter支持。如果你刚开始学Python,建议先熟悉基本语法和Jupyter Notebook的使用——这对交互式数据分析特别友好。
2.3 认证流程实现
Spotify使用OAuth 2.0认证,spotipy库已经封装了大部分复杂流程。这是我的认证代码模板:
python复制import spotipy
from spotipy.oauth2 import SpotifyOAuth
from dotenv import load_dotenv
import os
load_dotenv() # 加载.env文件中的环境变量
# 初始化Spotify客户端
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
client_id=os.getenv("SPOTIFY_CLIENT_ID"),
client_secret=os.getenv("SPOTIFY_CLIENT_SECRET"),
redirect_uri="http://localhost:8888/callback",
scope="user-library-read user-top-read user-read-recently-played"
))
将Client ID和Secret保存在项目根目录的.env文件中:
code复制SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
避坑指南:scope参数决定了能访问的数据范围,如果漏掉需要的scope,后续调用会报权限错误。最常见的包括user-library-read(获取收藏歌曲)、user-top-read(获取最常播放)和user-read-recently-played(最近播放记录)。
3. 获取并解析听歌数据
3.1 获取最近播放的歌曲
分析听歌习惯最好的起点是最近播放记录:
python复制def get_recently_played(limit=50):
results = sp.current_user_recently_played(limit=limit)
tracks = []
for item in results['items']:
track = item['track']
played_at = item['played_at']
tracks.append({
'id': track['id'],
'name': track['name'],
'artist': ', '.join([a['name'] for a in track['artists']]),
'album': track['album']['name'],
'duration_ms': track['duration_ms'],
'popularity': track['popularity'],
'played_at': played_at,
'timestamp': pd.to_datetime(played_at)
})
return pd.DataFrame(tracks)
这个函数返回一个包含最近播放歌曲信息的DataFrame,包含:
- 歌曲基本信息(名称、艺人、专辑)
- 元数据(时长、流行度)
- 播放时间(精确到毫秒的时间戳)
实操心得:Spotify API对请求频率有限制(每分钟约300次),获取大量历史数据时需要添加延迟。我通常会在循环中添加time.sleep(0.1)避免触发限制。
3.2 获取收藏的歌曲和专辑
了解你的音乐库构成也很重要:
python复制def get_saved_tracks(limit=50):
results = sp.current_user_saved_tracks(limit=limit)
tracks = []
for item in results['items']:
track = item['track']
added_at = item['added_at']
tracks.append({
'id': track['id'],
'name': track['name'],
'artist': ', '.join([a['name'] for a in track['artists']]),
'album': track['album']['name'],
'duration_ms': track['duration_ms'],
'popularity': track['popularity'],
'added_at': added_at,
'timestamp': pd.to_datetime(added_at)
})
return pd.DataFrame(tracks)
3.3 获取音频特征数据
Spotify为每首歌提供了详细的音频特征分析,这是最有价值的数据之一:
python复制def get_audio_features(track_ids):
features = []
for i in range(0, len(track_ids), 100): # 每次最多100首
batch = track_ids[i:i+100]
features.extend(sp.audio_features(batch))
return pd.DataFrame(features)
返回的特征包括:
- 声学特征:acousticness, instrumentalness
- 节奏特征:danceability, tempo, time_signature
- 情绪特征:valence (快乐程度), energy
- 音乐属性:mode, key, loudness
4. 数据分析与可视化
4.1 听歌时间模式分析
我首先分析了自己一周内不同时段的听歌偏好:
python复制def analyze_listening_patterns(df):
# 提取小时和星期几
df['hour'] = df['timestamp'].dt.hour
df['day_of_week'] = df['timestamp'].dt.day_name()
# 绘制听歌时间分布
plt.figure(figsize=(12, 6))
sns.histplot(data=df, x='hour', bins=24, kde=True)
plt.title('听歌时间分布')
plt.xlabel('小时')
plt.ylabel('播放次数')
plt.show()
# 星期几的听歌量
plt.figure(figsize=(10, 5))
day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
sns.countplot(data=df, x='day_of_week', order=day_order)
plt.title('一周听歌分布')
plt.xlabel('星期几')
plt.ylabel('播放次数')
plt.show()
分析结果让我大吃一惊:我总以为自己晚上听歌最多,实际上下午3-5点才是高峰;周一的播放量明显低于其他工作日——这可能与我的工作节奏有关。
4.2 音乐偏好分析
结合音频特征数据,可以量化自己的音乐偏好:
python复制def analyze_music_preferences(features_df):
# 选择主要特征
features = ['danceability', 'energy', 'valence', 'acousticness', 'instrumentalness']
# 计算平均值
avg_features = features_df[features].mean().to_frame('平均值').T
# 绘制雷达图
angles = np.linspace(0, 2*np.pi, len(features), endpoint=False)
angles = np.concatenate((angles, [angles[0]]))
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, polar=True)
values = avg_features.values[0]
values = np.concatenate((values, [values[0]]))
ax.plot(angles, values, 'o-', linewidth=2)
ax.fill(angles, values, alpha=0.25)
ax.set_thetagrids(angles[:-1] * 180/np.pi, features)
ax.set_title('音乐特征雷达图', size=20, y=1.1)
plt.show()
return avg_features
这个分析揭示了我的"音乐指纹":高energy和valence说明偏好积极向上的歌曲,低instrumentalness表明主要听人声为主的音乐。
4.3 艺人多样性分析
使用以下代码评估自己听歌的多样性:
python复制def analyze_artist_diversity(tracks_df, top_n=20):
# 统计艺人出现频率
artist_counts = tracks_df['artist'].value_counts().head(top_n)
# 计算多样性指数
total = artist_counts.sum()
proportions = artist_counts / total
diversity_index = 1 - sum(proportions ** 2) # Simpson多样性指数
# 绘制艺人分布
plt.figure(figsize=(12, 8))
artist_counts.plot(kind='barh')
plt.title(f'Top {top_n}艺人 (多样性指数: {diversity_index:.3f})')
plt.xlabel('播放次数')
plt.ylabel('艺人')
plt.gca().invert_yaxis()
plt.show()
return diversity_index
我的多样性指数只有0.85(最大为1),说明听歌集中在少数艺人——这促使我有意识地探索更多新艺人。
5. 高级分析与个性化洞察
5.1 创建听歌情绪时间线
结合音频特征中的valence(快乐程度)和energy,可以可视化听歌情绪变化:
python复制def create_mood_timeline(df, features_df):
# 合并播放记录和音频特征
merged = pd.merge(df, features_df, left_on='id', right_on='id')
# 计算每日平均情绪
daily_mood = merged.resample('D', on='timestamp').agg({
'valence': 'mean',
'energy': 'mean'
})
# 绘制情绪时间线
plt.figure(figsize=(15, 6))
plt.plot(daily_mood.index, daily_mood['valence'], label='快乐程度', color='green')
plt.plot(daily_mood.index, daily_mood['energy'], label='能量', color='orange')
plt.fill_between(daily_mood.index, daily_mood['valence'], alpha=0.2, color='green')
plt.fill_between(daily_mood.index, daily_mood['energy'], alpha=0.2, color='orange')
plt.title('每日听歌情绪变化')
plt.xlabel('日期')
plt.ylabel('分数')
plt.legend()
plt.grid(True)
plt.show()
# 计算情绪与星期几的关联
merged['day_of_week'] = merged['timestamp'].dt.dayofweek
mood_by_day = merged.groupby('day_of_week').agg({
'valence': 'mean',
'energy': 'mean'
})
return daily_mood, mood_by_day
这个分析让我发现周末听的歌确实更"快乐",而周三的能量值最高——可能与健身习惯有关。
5.2 发现音乐舒适区与探索行为
量化自己的音乐探索行为:
python复制def analyze_exploration_behavior(df):
# 计算新歌尝试率
df['is_new'] = ~df['id'].duplicated()
monthly_new = df.resample('M', on='timestamp')['is_new'].mean()
# 计算平均艺人重复率
df['artist_first_occurrence'] = ~df['artist'].duplicated()
monthly_new_artists = df.resample('M', on='timestamp')['artist_first_occurrence'].mean()
# 绘制探索行为趋势
fig, ax = plt.subplots(2, 1, figsize=(12, 8))
monthly_new.plot(ax=ax[0], title='每月新歌比例', color='blue')
monthly_new_artists.plot(ax=ax[1], title='每月新艺人比例', color='red')
plt.tight_layout()
plt.show()
return {
'avg_new_song_rate': monthly_new.mean(),
'avg_new_artist_rate': monthly_new_artists.mean()
}
我的新歌尝试率只有35%,这意味着65%的时间都在听已经听过的歌——这个发现促使我创建了一个"探索播放列表",每周强制添加20首新歌。
5.3 构建个性化音乐档案
将所有分析整合成一个综合报告:
python复制def generate_personal_music_profile(tracks_df, features_df):
profile = {}
# 基础统计
profile['total_tracks'] = len(tracks_df)
profile['unique_artists'] = tracks_df['artist'].nunique()
profile['unique_albums'] = tracks_df['album'].nunique()
# 时间模式
profile['busiest_hour'] = tracks_df['hour'].value_counts().idxmax()
# 音乐特征
audio_profile = features_df[['danceability', 'energy', 'valence',
'acousticness', 'instrumentalness']].mean().to_dict()
profile.update(audio_profile)
# 多样性指标
profile['artist_diversity'] = analyze_artist_diversity(tracks_df, top_n=20)
# 探索行为
exploration = analyze_exploration_behavior(tracks_df)
profile.update(exploration)
return pd.Series(profile)
我的个性化档案显示:
- 平均每天听歌2.3小时
- 最常听歌时间:下午4点
- 音乐特征:高能量(0.72)、中等快乐(0.58)
- 艺人多样性指数:0.82
- 新歌尝试率:35%
6. 项目扩展与实用技巧
6.1 自动化定期分析
我设置了一个每周运行的脚本,自动更新分析结果:
python复制def weekly_report():
# 获取数据
recent_tracks = get_recently_played(limit=200)
features = get_audio_features(recent_tracks['id'].tolist())
# 分析
analyze_listening_patterns(recent_tracks)
analyze_music_preferences(features)
diversity = analyze_artist_diversity(recent_tracks)
# 保存结果
report = {
'date': pd.Timestamp.now().strftime('%Y-%m-%d'),
'tracks_played': len(recent_tracks),
'unique_artists': recent_tracks['artist'].nunique(),
'diversity_index': diversity
}
# 追加到历史记录
history = pd.read_csv('weekly_history.csv') if os.path.exists('weekly_history.csv') else pd.DataFrame()
history = history.append(report, ignore_index=True)
history.to_csv('weekly_history.csv', index=False)
return report
6.2 创建个性化推荐播放列表
基于分析结果自动生成推荐:
python复制def create_recommendation_playlist(sp, seed_tracks, target_features, limit=30):
recommendations = sp.recommendations(
seed_tracks=seed_tracks,
target_danceability=target_features['danceability'],
target_energy=target_features['energy'],
target_valence=target_features['valence'],
limit=limit
)
track_ids = [track['id'] for track in recommendations['tracks']]
user_id = sp.me()['id']
playlist = sp.user_playlist_create(
user=user_id,
name=f"推荐 {pd.Timestamp.now().strftime('%Y-%m-%d')}",
public=False
)
sp.playlist_add_items(playlist['id'], track_ids)
return playlist
6.3 遇到的坑与解决方案
-
API限流问题:
- 现象:频繁请求后返回429错误
- 解决:实现指数退避重试机制
python复制from time import sleep from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=4, max=10)) def safe_api_call(func, *args, **kwargs): return func(*args, **kwargs) -
数据不完整问题:
- 现象:部分老歌缺少音频特征
- 解决:添加缺失值处理
python复制features_df = features_df.dropna(subset=['danceability', 'energy']) -
时区问题:
- 现象:播放时间显示不正确
- 解决:统一转换为本地时区
python复制df['timestamp'] = pd.to_datetime(df['played_at']).dt.tz_convert('Asia/Shanghai')
这个项目最让我惊喜的是发现了自己都没意识到的听歌模式。比如我总认为自己音乐品味很"酷",但数据清楚地显示,下雨天我听得最多的是Taylor Swift——这大概就是数据的魅力,它不会说谎。
