1. 为什么分析Spotify听歌数据值得一试
作为一个长期使用Spotify的音乐爱好者,我发现自己经常在年底收到平台的"年度回顾"时感到惊喜——原来我这一年听了这么多冷门歌曲,或者在某个月份集中循环了某张专辑。这种发现让我萌生了自己分析听歌数据的想法。通过Python实现的个性化分析,远比平台提供的标准化报告更有趣:
- 你可以发现平台算法不会告诉你的听歌模式(比如工作日和周末的曲风差异)
- 能追踪自己音乐品味的演变轨迹(去年此时你最爱哪些艺术家)
- 甚至可以通过数据发现自己"伪粉"行为(收藏但从未完整听完的专辑)
更重要的是,这还是一个绝佳的Python实战项目。从API调用、数据处理到可视化,涵盖了一个数据分析项目的完整流程,但复杂度又控制在初学者可接受的范围内。我最初做这个项目时,Spotify的API文档还是全英文的,现在国内开发者社区已经有了丰富的中文资料,入门门槛大大降低。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 获取数据:Spotify开发者平台实操指南
2.1 创建开发者应用
首先访问Spotify开发者平台,点击右上角"Dashboard"登录(需要使用你的Spotify账号)。在控制台页面选择"Create an App",填写应用名称和描述(比如"个人听歌分析工具"),勾选开发者条款后点击创建。
创建成功后,记下两个关键信息:
- Client ID:类似
5f3f8d9e1c2745a8b66f8c7d01e2b345 - Client Secret:类似
e4r5t6y7u8i9o0p1a2s3d4f5g6h7j8k
重要提示:Client Secret相当于密码,绝对不能上传到GitHub等公开平台。我习惯将其保存在本地环境变量中。
2.2 获取API访问令牌
Spotify的API采用OAuth 2.0认证。对于个人分析项目,我们可以使用"客户端凭证流"简化流程。以下是获取令牌的Python代码:
python复制import base64
import requests
client_id = '你的Client_ID'
client_secret = '你的Client_Secret'
# 将ID和Secret编码为Base64
credentials = f"{client_id}:{client_secret}"
encoded_credentials = base64.b64encode(credentials.encode()).decode()
# 请求令牌
token_url = 'https://accounts.spotify.com/api/token'
headers = {
'Authorization': f'Basic {encoded_credentials}',
'Content-Type': 'application/x-www-form-urlencoded'
}
data = {'grant_type': 'client_credentials'}
response = requests.post(token_url, headers=headers, data=data)
access_token = response.json().get('access_token')
这个令牌通常有效期为1小时,过期后需要重新获取。对于长期分析项目,建议实现自动刷新机制。
2.3 获取个人听歌历史
要获取个人收听数据,需要使用授权码流(Authorization Code Flow)。这需要设置一个回调URL,对于本地开发,可以使用http://localhost:8888/callback。具体步骤:
- 在应用设置中添加回调URL
- 用浏览器访问授权端点获取code
- 用code交换access_token和refresh_token
以下是获取最近播放记录的示例:
python复制import requests
headers = {
'Authorization': f'Bearer {access_token}'
}
# 获取最近50条播放记录
recently_played = requests.get(
'https://api.spotify.com/v1/me/player/recently-played?limit=50',
headers=headers
).json()
# 获取用户收藏的歌曲
saved_tracks = requests.get(
'https://api.spotify.com/v1/me/tracks?limit=50',
headers=headers
).json()
3. 数据清洗与结构化处理
3.1 原始数据结构解析
从API获取的原始数据通常是嵌套的JSON格式。以单条播放记录为例:
json复制{
"track": {
"id": "11dFghVXANMlKmJXsNCbNl",
"name": "Cut To The Feeling",
"artists": [
{
"id": "6sFIWsNpZYqfjUpaCgueju",
"name": "Carly Rae Jepsen"
}
],
"duration_ms": 207959,
"explicit": false,
"popularity": 69
},
"played_at": "2023-07-20T15:30:00Z",
"context": {
"type": "playlist",
"href": "https://api.spotify.com/v1/playlists/37i9dQZF1DXcBWIGoYBM5M"
}
}
我们需要将其扁平化为更适合分析的表格结构。使用pandas的json_normalize可以轻松实现:
python复制import pandas as pd
from pandas import json_normalize
# 将播放记录转换为DataFrame
df_plays = json_normalize(
recently_played['items'],
meta=['played_at'],
record_prefix='track.'
)
# 选择需要的列
cols_to_keep = [
'played_at',
'track.id',
'track.name',
'track.artists',
'track.duration_ms',
'track.popularity'
]
df_plays = df_plays[cols_to_keep]
3.2 时间数据处理技巧
播放时间(played_at)是UTC格式字符串,我们需要将其转换为本地时区并提取有用信息:
python复制from pytz import timezone
from datetime import datetime
# 转换为datetime对象
df_plays['played_at'] = pd.to_datetime(df_plays['played_at'])
# 转换为本地时区(以上海为例)
df_plays['played_at'] = df_plays['played_at'].dt.tz_convert('Asia/Shanghai')
# 提取日期和时间组件
df_plays['play_date'] = df_plays['played_at'].dt.date
df_plays['play_hour'] = df_plays['played_at'].dt.hour
df_plays['day_of_week'] = df_plays['played_at'].dt.dayofweek # 周一=0,周日=6
3.3 艺术家信息展开
每条记录可能包含多个艺术家,我们需要特殊处理:
python复制def extract_artists(artists_list):
return [artist['name'] for artist in artists_list]
df_plays['artists_names'] = df_plays['track.artists'].apply(extract_artists)
df_plays['primary_artist'] = df_plays['artists_names'].str[0]
4. 探索性分析与可视化
4.1 基础统计指标
先看一些基础统计量,对数据有个整体认识:
python复制print(f"分析时间段: {df_plays['play_date'].min()} 至 {df_plays['play_date'].max()}")
print(f"总播放次数: {len(df_plays)}")
print(f"独特歌曲数量: {df_plays['track.id'].nunique()}")
print(f"独特艺术家数量: {df_plays['primary_artist'].nunique()}")
# 播放时长统计
total_ms = df_plays['track.duration_ms'].sum()
hours = total_ms / (1000 * 60 * 60)
print(f"总收听时长: {hours:.2f} 小时")
4.2 听歌时间模式分析
使用matplotlib绘制听歌时间分布:
python复制import matplotlib.pyplot as plt
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 按小时分布
hourly_counts = df_plays.groupby('play_hour').size()
plt.figure(figsize=(12, 6))
hourly_counts.plot(kind='bar', color='#1DB954')
plt.title('一天中不同时段的听歌频率')
plt.xlabel('小时')
plt.ylabel('播放次数')
plt.xticks(rotation=0)
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.show()
![听歌时间分布示例图:显示早高峰和晚高峰的听歌高峰时段]
4.3 艺术家与歌曲排行
统计最常听的艺术家和歌曲:
python复制top_artists = df_plays['primary_artist'].value_counts().head(10)
top_songs = df_plays.groupby(['track.name', 'primary_artist']).size().sort_values(ascending=False).head(10)
plt.figure(figsize=(10, 8))
top_artists.plot(kind='barh', color='#1DB954')
plt.title('最常听的10位艺术家')
plt.xlabel('播放次数')
plt.gca().invert_yaxis()
plt.show()
4.4 高级分析:音乐特征探索
Spotify为每首歌提供了音频特征数据,如舞蹈性(danceability)、能量(energy)等。我们可以获取这些数据进行更深层次的分析:
python复制# 获取歌曲特征
track_ids = df_plays['track.id'].unique()
features = []
for i in range(0, len(track_ids), 50): # 每次最多查询50首
batch = track_ids[i:i+50]
features_batch = requests.get(
f'https://api.spotify.com/v1/audio-features?ids={",".join(batch)}',
headers=headers
).json()
features.extend(features_batch['audio_features'])
df_features = pd.DataFrame(features)
然后可以分析不同时段的音乐特征差异:
python复制# 合并播放记录和特征数据
df_merged = pd.merge(df_plays, df_features, left_on='track.id', right_on='id')
# 工作日vs周末的音乐特征对比
weekend = df_merged['day_of_week'] >= 5 # 5和6代表周六周日
df_weekday = df_merged[~weekend]
df_weekend = df_merged[weekend]
print("工作日平均能量值:", df_weekday['energy'].mean())
print("周末平均能量值:", df_weekend['energy'].mean())
5. 项目扩展与实用技巧
5.1 定期自动更新数据
要实现数据的定期收集,可以设置一个简单的定时任务:
python复制import schedule
import time
def collect_data():
# 获取新播放记录
new_data = get_recently_played()
# 追加到现有数据文件
existing = pd.read_csv('spotify_history.csv')
updated = pd.concat([existing, new_data]).drop_duplicates()
updated.to_csv('spotify_history.csv', index=False)
print(f"{time.ctime()}: 数据已更新,总记录数{len(updated)}")
# 每天凌晨3点运行
schedule.every().day.at("03:00").do(collect_data)
while True:
schedule.run_pending()
time.sleep(60)
5.2 使用Spotipy库简化流程
Spotipy是Spotify官方的Python库,可以简化很多操作:
python复制import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = "user-read-recently-played user-library-read"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
# 获取最近播放
results = sp.current_user_recently_played(limit=50)
5.3 部署为Web应用
使用Streamlit可以快速创建交互式仪表盘:
python复制import streamlit as st
st.title('我的Spotify听歌分析')
time_range = st.selectbox('选择时间范围', ['最近4周', '最近6个月', '所有时间'])
# 根据选择过滤数据
if time_range == '最近4周':
filtered = df[df['played_at'] > pd.Timestamp.now() - pd.Timedelta(weeks=4)]
elif time_range == '最近6个月':
filtered = df[df['played_at'] > pd.Timestamp.now() - pd.Timedelta(weeks=26)]
else:
filtered = df
# 显示图表
st.bar_chart(filtered.groupby('primary_artist').size().nlargest(10))
5.4 遇到的坑与解决方案
-
API速率限制:Spotify API每分钟最多允许300次请求。解决方法:在密集请求间添加time.sleep(0.5)延迟,或使用缓存。
-
时区问题:所有时间戳都是UTC,必须显式转换为本地时间。我曾在分析时发现"凌晨3点的听歌高峰",其实是UTC转换错误。
-
数据不完整:最近播放API最多只能获取最近50条记录。长期追踪需要定期收集数据。我的解决方案是设置了一个每天运行的脚本。
-
授权令牌过期:用户令牌通常1小时后过期。解决方案是获取refresh_token并在令牌过期时自动刷新:
python复制def refresh_token(refresh_token):
payload = {
'grant_type': 'refresh_token',
'refresh_token': refresh_token
}
response = requests.post(token_url, auth=(client_id, client_secret), data=payload)
return response.json()['access_token']
这个项目最让我惊喜的发现是:我原以为自己主要在工作时听轻音乐,但数据显示周末深夜才是我的"重金属时间"。数据不会说谎,它揭示了我们自己都没意识到的行为模式。
