1. 项目概述:当Python遇上你的音乐记忆
作为一名长期使用Spotify的音乐爱好者兼Python开发者,我最近发现了一个有趣的结合点——用Python脚本解析自己的听歌数据。这不仅仅是简单的数据统计,而是通过编程语言重新发现自己的音乐品味演变过程。想象一下,你能精确知道自己在2020年疫情期间循环播放了哪些治愈系歌曲,或是比较工作日与周末的听歌风格差异,甚至发现那些"以为自己喜欢但实际上很少听"的歌手。
Spotify官方确实提供年度回顾(Wrapped)功能,但那只是冰山一角。通过API获取原始数据后,我们可以进行更自由的分析:从基础的播放次数统计,到复杂的音乐特征聚类,再到基于时间序列的听歌习惯分析。本文将带你从零开始,使用Python生态中的利器(requests、pandas、spotipy等)构建完整的分析流水线,最终生成比官方报告更个性化的音乐洞察。
2. 环境准备与数据获取
2.1 创建Spotify开发者应用
首先访问Spotify开发者仪表板,点击"Create App"按钮。填写应用名称(如"My Listening Analysis")和描述后,记下生成的Client ID和Client Secret。这两个密钥将用于所有API请求的身份验证。
重要提示:千万不要将Client Secret直接暴露在代码中!建议使用环境变量或配置文件管理,本文示例将使用python-dotenv包实现安全加载。
安装必要依赖:
bash复制pip install spotipy pandas python-dotenv matplotlib seaborn
2.2 配置认证流程
在项目目录创建.env文件存储凭证:
ini复制SPOTIPY_CLIENT_ID='your_client_id'
SPOTIPY_CLIENT_SECRET='your_client_secret'
SPOTIPY_REDIRECT_URI='http://localhost:8888/callback'
然后建立认证模块:
python复制import spotipy
from spotipy.oauth2 import SpotifyOAuth
from dotenv import load_dotenv
import os
load_dotenv()
scope = "user-library-read user-read-recently-played user-top-read"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
scope=scope,
redirect_uri=os.getenv("SPOTIPY_REDIRECT_URI"),
client_id=os.getenv("SPOTIPY_CLIENT_ID"),
client_secret=os.getenv("SPOTIPY_CLIENT_SECRET"),
show_dialog=True
))
首次运行时会自动打开浏览器完成OAuth授权流程,之后会生成缓存文件避免重复登录。
2.3 获取核心数据端点
Spotify API提供了多个关键端点供我们挖掘:
- 近期播放记录:
sp.current_user_recently_played(limit=50) - 收藏歌曲:
sp.current_user_saved_tracks(limit=50) - 常听艺人:
sp.current_user_top_artists(time_range='medium_term') - 常听曲目:
sp.current_user_top_tracks(time_range='long_term')
其中time_range参数可选short_term(约4周)、medium_term(约6个月)和long_term(数年)。
3. 数据清洗与结构化处理
3.1 构建完整播放历史
由于API单次请求最多返回50条记录,我们需要分页获取完整历史:
python复制def get_all_recent_tracks(days=30):
results = []
before = int(time.time()) * 1000
while True:
tracks = sp.current_user_recently_played(limit=50, before=before)
if not tracks['items']:
break
results.extend(tracks['items'])
before = tracks['items'][-1]['played_at']
# 控制获取最近N天的数据
if datetime.fromtimestamp(before/1000) < datetime.now() - timedelta(days=days):
break
return results
3.2 提取音频特征增强分析
原始数据只包含基础元信息,要获得更专业的分析需要获取每首歌的音频特征:
python复制def get_audio_features(track_ids):
features = []
for i in range(0, len(track_ids), 50):
batch = track_ids[i:i+50]
features.extend(sp.audio_features(batch))
return features
这些特征包括:
- danceability(舞蹈性):0.0-1.0
- energy(能量感):0.0-1.0
- valence(愉悦度):0.0-1.0
- tempo(BPM):通常50-200
- time_signature(拍号):3/4、4/4等
- mode(调式):0=小调,1=大调
3.3 构建分析用DataFrame
使用pandas整合所有数据:
python复制import pandas as pd
def create_tracks_df(recent_tracks):
rows = []
for item in recent_tracks:
track = item['track']
played_at = item['played_at']
rows.append({
'played_at': played_at,
'id': track['id'],
'name': track['name'],
'artist': ', '.join([a['name'] for a in track['artists']]),
'duration_ms': track['duration_ms'],
'popularity': track['popularity'],
'explicit': track['explicit']
})
return pd.DataFrame(rows)
4. 基础分析可视化
4.1 听歌时间分布分析
首先分析不同时段的听歌偏好:
python复制import matplotlib.pyplot as plt
df['hour'] = pd.to_datetime(df['played_at']).dt.hour
hourly_counts = df['hour'].value_counts().sort_index()
plt.figure(figsize=(12,6))
sns.barplot(x=hourly_counts.index, y=hourly_counts.values, palette='viridis')
plt.title('Listening Activity by Hour of Day')
plt.xlabel('Hour')
plt.ylabel('Number of Plays')
plt.xticks(rotation=45)
plt.show()
典型发现可能包括:
- 通勤时段(8-9AM,5-7PM)的播放高峰
- 深夜(12-3AM)的特定类型音乐偏好
- 工作时段(10AM-4PM)的背景音乐选择
4.2 艺人播放频率分析
使用词云展示最常收听的艺人:
python复制from wordcloud import WordCloud
artist_counts = df['artist'].str.split(', ').explode().value_counts()
wc = WordCloud(width=800, height=400, background_color='white').generate_from_frequencies(artist_counts)
plt.figure(figsize=(15,8))
plt.imshow(wc, interpolation='bilinear')
plt.axis('off')
plt.show()
4.3 音乐特征雷达图
对比工作日与周末的音乐特征差异:
python复制features = ['danceability', 'energy', 'valence', 'acousticness', 'instrumentalness']
weekday_mean = df[df['weekday'] < 5][features].mean()
weekend_mean = df[df['weekday'] >= 5][features].mean()
angles = np.linspace(0, 2*np.pi, len(features), endpoint=False)
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111, polar=True)
ax.plot(angles, weekday_mean.values, 'o-', label='Weekday')
ax.plot(angles, weekend_mean.values, 'o-', label='Weekend')
ax.fill(angles, weekday_mean.values, alpha=0.25)
ax.fill(angles, weekend_mean.values, alpha=0.25)
ax.set_thetagrids(angles * 180/np.pi, features)
ax.set_title('Audio Features: Weekday vs Weekend')
plt.legend()
plt.show()
5. 高级分析技巧
5.1 基于K-Means的音乐聚类
将歌曲按音频特征分组,发现潜在的音乐偏好模式:
python复制from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
X = df[features].values
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
kmeans = KMeans(n_clusters=5, random_state=42)
df['cluster'] = kmeans.fit_predict(X_scaled)
cluster_profiles = df.groupby('cluster')[features].mean()
5.2 听歌行为时间序列分析
使用Prophet预测未来的听歌趋势:
python复制from prophet import Prophet
daily_counts = df.resample('D', on='played_at').size().reset_index()
daily_counts.columns = ['ds', 'y']
model = Prophet(seasonality_mode='multiplicative')
model.fit(daily_counts)
future = model.make_future_dataframe(periods=30)
forecast = model.predict(future)
model.plot(forecast);
5.3 构建个性化推荐系统
基于协同过滤算法推荐可能喜欢的新歌:
python复制from surprise import Dataset, Reader, KNNBasic
# 构建用户-歌曲-播放次数矩阵
ratings = df.groupby(['user_id', 'track_id']).size().reset_index(name='plays')
reader = Reader(rating_scale=(1, ratings['plays'].max()))
data = Dataset.load_from_df(ratings[['user_id', 'track_id', 'plays']], reader)
algo = KNNBasic(sim_options={'user_based': False})
algo.fit(data.build_full_trainset())
6. 实战案例:我的2023年度音乐报告
以下是我个人数据分析的真实发现:
- 晨间效率歌单:工作日上午9-11点,高频出现lo-fi hip hop和古典钢琴曲(instrumentalness > 0.8)
- 通勤模式:下班时段的播放列表明显更高能(energy > 0.7)且节奏更快(tempo > 120)
- 季节性变化:夏季的valence均值比冬季高0.15,验证了"夏日欢快曲"现象
- 隐藏偏好:虽然收藏了大量摇滚乐,但实际播放最多的是爵士三重奏(cluster分析发现)
7. 数据导出与分享
将分析结果导出为交互式HTML报告:
python复制import plotly.express as px
from kaleido.scopes.plotly import PlotlyScope
fig = px.sunburst(df, path=['cluster', 'artist'], values='play_count')
scope = PlotlyScope()
with open("report.html", "wb") as f:
f.write(scope.transform(fig, format='html'))
还可以使用Flask构建简单的Web仪表盘:
python复制from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def dashboard():
top_artists = df['artist'].value_counts().nlargest(10).to_dict()
return render_template('dashboard.html', artists=top_artists)
8. 避坑指南与性能优化
- API限速处理:
python复制from time import sleep
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
def safe_api_call(func, *args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
if e.code == 429:
sleep(int(e.headers['Retry-After']))
raise
- 大数据量缓存策略:
python复制import pickle
from pathlib import Path
cache_dir = Path('spotify_cache')
cache_dir.mkdir(exist_ok=True)
def get_with_cache(endpoint, params):
cache_file = cache_dir / f"{endpoint}_{hash(frozenset(params.items()))}.pkl"
if cache_file.exists():
return pickle.loads(cache_file.read_bytes())
data = safe_api_call(getattr(sp, endpoint), **params)
cache_file.write_bytes(pickle.dumps(data))
return data
- 内存优化技巧:
- 对于大型数据集,使用
dtype优化:
python复制dtypes = {
'danceability': 'float32',
'duration_ms': 'uint32',
'popularity': 'uint8'
}
df = df.astype(dtypes)
9. 扩展思路:当音乐数据遇上机器学习
- 情绪识别模型:训练CNN模型根据音频频谱预测听歌时的情绪状态
- 自动歌单生成:使用GPT-3 API为聚类结果生成描述性标题和封面文案
- 跨平台分析:整合Last.fm、Apple Music等平台数据构建统一视图
- 实时仪表盘:使用FastAPI + WebSocket实现播放数据的实时可视化
python复制# 示例:使用librosa提取MFCC特征用于深度学习
import librosa
def extract_features(file_path):
y, sr = librosa.load(file_path)
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
return mfcc.flatten()
10. 个人音乐档案的长期价值
坚持收集听歌数据一年后,我发现这些记录已经成为珍贵的数字记忆:
- 通过"2020年3月歌单"准确回忆起疫情初期的居家生活
- 发现某些艺人的播放量随着个人生活事件明显波动
- 识别出音乐品味发生显著变化的转折月份
- 建立了个性化的"心情-音乐"映射关系数据库
建议设置定期自动化任务(如每周通过GitHub Actions运行分析脚本),构建个人的音乐编年史。本文所有代码已适配Python 3.8+环境,完整项目结构建议如下:
code复制spotify-analysis/
├── data/ # 原始数据缓存
├── notebooks/ # Jupyter分析笔记
├── reports/ # 生成的HTML/PDF报告
├── app/ # Flask/FastAPI应用
├── config.py # 配置文件
├── analysis.py # 主分析模块
└── requirements.txt # 依赖清单