使用 Python 和 OAuth 2.0 构建 Spotify 音乐时光机

发布: (2026年2月13日 GMT+8 02:22)
14 分钟阅读
原文: Dev.to

Source: Dev.to

如果你和大多数人一样,你根本不知道这些歌到底在哪里。那些歌曲已经消失——不是从 Spotify 的服务器上消失,而是从你的记忆中消失。你在 2022 年痴迷的播放列表已经被遗忘,而你当前的最爱则占据了队列的主导。

Spotify 会提供短期的回顾和推荐,但要重新聆听定义你人生早期章节的音乐却出奇地困难。你可以记得一个时代,却记不清当时循环播放的具体曲目。

在本教程中,你将构建一个解决方案。你将使用 OAuth 2.0 对 Spotify 进行身份验证,获取个人的聆听数据,并以编程方式创建播放列表——全部使用 Python 完成。

完成后,你将拥有:

  • 一个可运行的 Spotify OAuth 2.0 集成
  • 一个能够在你的 Spotify 账户中创建真实播放列表的脚本(约 30 行代码)
  • 对 Spotify 时间范围和音频特征的理解
  • 一个 “被遗忘的宝石” 功能的基础,帮助你重新发现那些曾经喜爱但已停止收听的歌曲

先决条件: 基础的 Python 知识(变量、函数、列表)。一个 Spotify 账户(免费或付费)。仅此而已。

Source:

第一步:获取 Spotify 开发者凭证

每个使用 Spotify API 的应用都需要凭证:客户端 ID客户端密钥。它们用于标识你的应用并启用 OAuth 认证。

  1. 前往 Spotify 开发者仪表板
    访问并使用你的 Spotify 账户登录。

  2. 创建应用
    点击 “Create app” 并填写表单:

    • 应用名称: Music Time Machine(或你喜欢的任意名称)
    • 应用描述: Personal music analytics and playlist generator
    • 重定向 URI: http://localhost:8888/callback(必须完全相同——OAuth 授权后会重定向到此)
    • 使用的 API: 勾选 “Web API”
  3. 复制你的凭证
    创建应用后,点击 “Settings”。 你会看到显示的 Client ID。点击 “View client secret” 以显示 Client Secret。将两者复制下来——稍后会用到。

为什么使用 localhost:8888/callback
在授权应用后,Spotify 会将浏览器重定向到该 URI 并附带授权码。我们将使用的 spotipy 库会在本地 8888 端口启动一个临时服务器,以捕获重定向并提取代码。这是标准的 OAuth 流程;重定向 URI 并不需要是公开的服务器。

步骤 2:安装依赖

您需要两个 Python 包:

pip install spotipy python-dotenv
  • spotipy – 一个 Spotify API 包装器,负责 OAuth 令牌交换、令牌刷新以及请求格式化。
  • python-dotenv – 从 .env 文件加载环境变量。

第3步:安全存储凭证

  1. 在项目目录中创建 .env 文件。此文件用于存放机密信息,绝不能提交到版本控制。

    SPOTIPY_CLIENT_ID=your_client_id_here
    SPOTIPY_CLIENT_SECRET=your_client_secret_here
    SPOTIPY_REDIRECT_URI=http://localhost:8888/callback

    用在第 1 步中复制的凭证替换占位符值。

  2. .env 添加到 .gitignore(以及其他不想纳入版本控制的文件):

    .env
    __pycache__/
    *.pyc
    .cache
    *.db

    当你运行 OAuth 流程时会生成 .cache 文件——它在本地保存访问令牌。也请将其排除在版本控制之外。

第4步:创建你的第一个播放列表

此脚本使用 Spotify 进行身份验证,获取你的热门曲目,并在你的账户中创建一个实际的播放列表。

"""
Quick Start: Create your first Spotify playlist
Demonstrates OAuth 2.0 authentication and basic API usage
"""
import os
from dotenv import load_dotenv
import spotipy
from spotipy.oauth2 import SpotifyOAuth

# Load credentials from .env file
load_dotenv()

# Define the permissions we need
scope = "user-top-read playlist-modify-public playlist-modify-private"

# Create Spotify client with OAuth
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))

# Get current user info
user = sp.current_user()
print(f"Authenticated as: {user['display_name']}")

# Fetch top tracks from last 4 weeks
print("\nFetching your top tracks...")
top_tracks = sp.current_user_top_tracks(limit=20, time_range='short_term')

# Extract track URIs (Spotify's unique identifiers) and names
track_uris = [track['uri'] for track in top_tracks['items']]
track_names = [track['name'] for track in top_tracks['items']]

# Create a new playlist
print("\nCreating playlist...")
playlist = sp.user_playlist_create(
    user=user['id'],
    name="My Top Tracks - Quick Start",
    public=False,
    description="Created by Music Time Machine - My current favorites"
)

# Add tracks to the playlist
sp.playlist_add_items(playlist['id'], track_uris)

print(f"\nSuccess! Created playlist with {len(track_uris)} tracks")
print(f"Playlist URL: {playlist['external_urls']['spotify']}")
print("\nYour top tracks right now:")
for i, name in enumerate(track_names, 1):
    print(f"  {i}. {name}")
  1. 将脚本保存为 quick_start.py

  2. 运行它:

    python quick_start.py

接下来会发生什么: 你的浏览器会打开 Spotify 的授权页面。点击 “同意” 后,浏览器会重定向到 localhost:8888/callbackspotipy 捕获授权码,将其交换为访问令牌,随后脚本继续执行。

检查你的 Spotify 应用——播放列表已经出现。你刚刚完成了对真实世界 API 的集成! 🎉

随意在此基础上扩展:获取更长期的热门曲目(medium_termlong_term),分析音频特征,或构建一个“被遗忘的宝石”播放列表,展示你过去喜爱但最近未播放的曲目。

Source:

OAuth API,获取个性化数据,并在你的账户中创建内容——全部只需约 30 行 Python

刚刚发生了什么:OAuth 2.0 速览(60 秒)

当你运行该脚本时,Spotipy 完成了完整的 OAuth 2.0 授权码流程:

步骤Spotipy 的操作
授权请求在浏览器中打开 https://accounts.spotify.com/authorize,携带你的 Client ID、请求的 scopes 和重定向 URI。
用户同意显示应用需要的权限;你点击 Agree(同意)。
授权码Spotify 将临时代码附在 URL 中重定向到 http://localhost:8888/callback
令牌交换Spotipy 将代码(以及你的 Client ID 与 Secret)发送到 Spotify 的 token 端点,获取 access token(访问令牌)和 refresh token(刷新令牌)。
API 调用随后的每一次调用(current_user_top_tracksuser_playlist_create 等)都在 Authorization: Bearer … 头部携带访问令牌。
令牌缓存令牌被保存到 .cache 文件中。后续运行时 Spotipy 会读取缓存,避免再次授权。当访问令牌过期(约 1 小时)时,Spotipy 会自动使用刷新令牌获取新的访问令牌。

这正是为 “Sign in with Google”、GitHub 集成以及几乎所有第三方应用连接你的账户所使用的同一套 OAuth 流程。模式在各处完全相同——唯一的区别在于具体的端点 URL。

理解作用域:权限的含义

scope = "user-top-read playlist-modify-public playlist-modify-private"
范围权限
user-top-read读取你的热门曲目和艺术家。如果没有此权限,sp.current_user_top_tracks() 会返回 403 Forbidden
playlist-modify-public创建和修改 公开 播放列表。
playlist-modify-private创建和修改 私密 播放列表(脚本使用 public=False 创建私密播放列表)。

最小特权原则 – 只请求你实际需要的权限。Spotify 用户在授权页面上会看到你请求的具体权限。请求你从未使用的作用域(例如 user-library-modify)会被视为风险信号。

进一步探索:时间范围与被遗忘的佳作

Spotify 的 current_user_top_tracks() 方法接受一个 time_range 参数:

时间范围大约时间段捕获的内容
short_term~4 周当前痴迷
medium_term~6 个月持续的最爱
long_term数年全时模式

通过比较这些范围,你可以发现那些被遗忘的歌曲:

# Fetch top tracks for each time range
short_term = sp.current_user_top_tracks(limit=50, time_range='short_term')
long_term  = sp.current_user_top_tracks(limit=50, time_range='long_term')

# Extract track IDs into sets
short_ids = {track['id'] for track in short_term['items']}
long_ids  = {track['id'] for track in long_term['items']}

# Forgotten gems: songs in your long‑term favorites that you haven't listened to recently
forgotten = long_ids - short_ids

print(f"You loved {len(forgotten)} tracks long‑term but haven't heard them recently")

创建 “被遗忘的佳作” 歌单

# Get full track details for forgotten gems
forgotten_tracks = [
    track for track in long_term['items']
    if track['id'] in forgotten
]

# Create the playlist
user = sp.current_user()
playlist = sp.user_playlist_create(
    user=user['id'],
    name="Forgotten Gems",
    public=False,
    description="Songs I loved but forgot about – rediscovered by Music Time Machine"
)

track_uris = [track['uri'] for track in forgotten_tracks]
sp.playlist_add_items(playlist['id'], track_uris)

print(f"\nCreated 'Forgotten Gems' with {len(forgotten_tracks)} tracks")
for track in forgotten_tracks:
    print(f"  - {track['name']}{track['artists'][0]['name']}")

更进一步:音频特征与情绪播放列表

Spotify 会分析每首曲目并为音频特征分配数值分数。你可以通过 API 获取这些信息:

# Fetch audio features for your top tracks
tracks = sp.current_user_top_tracks(limit=50)['items']
track_ids = [track['id'] for track in tracks]
features_list = sp.audio_features(track_ids)

# Show a few examples
for track, features in zip(tracks[:3], features_list[:3]):
    if features:
        print(f"\n{track['name']}{track['artists'][0]['name']}")
        print(f"  Energy:       {features['energy']:.2f}")
        print(f"  Valence:      {features['valence']:.2f} (happiness)")
        print(f"  Danceability: {features['danceability']:.2f}")
        print(f"  Tempo:        {features['tempo']:.0f} BPM")

关键音频特征分数(0.0 – 1.0,节奏除外)

特征含义
Energy强度与活跃度(例如,AC/DC ≈ 0.9,氛围音乐 ≈ 0.1)。
Valence音乐的幸福感(例如,“Happy” – 0.96,“Hurt” – 0.14)。
Danceability节拍的强度与规律性(放克/迪斯科得分高,自由爵士得分低)。
Tempo每分钟节拍数(对健身/跑步播放列表很有用)。
Acousticness作品是声学的可能性(相对于电子音乐)。
Instrumentalness预测是否有人声(得分高 → 器乐曲)。

示例:构建适合锻炼的播放列表

def matches_workout_profile(features):
    """Return True if a track looks good for a high‑intensity workout."""
    return (
        features['energy']      > 0.7 and
        features['danceability'] > 0.7 and
        features['tempo']       > 120
    )

# Filter top tracks for workout suitability
workout_tracks = [
    track['uri'] for track, feat in zip(tracks, features_list)
    if feat and matches_workout_profile(feat)
]

# Create the playlist
playlist = sp.user_playlist_create(
    user=user['id'],
    name="Workout Boost",
    public=False,
    description="High‑energy tracks for the gym – generated from my top‑50"
)

sp.playlist_add_items(playlist['id'], workout_tracks)

print(f"Created 'Workout Boost' with {len(workout_tracks)} tracks")
def matches_workout_profile(features):
    """Check if a track suits a workout playlist"""
    return (
        features['energy'] > 0.75 and
        features['valence'] > 0.50 and
        features['tempo'] > 140 and
        features['danceability'] > 0.6
    )

# Filter your top tracks for workout‑suitable songs
workout_tracks = [
    tracks[i] for i in range(len(tracks))
    if features_list[i] and matches_workout_profile(features_list[i])
]

print(f"Found {len(workout_tracks)} workout tracks from your top 50")

接下来该怎么做

在本教程中,你已经构建了一个可工作的 Spotify OAuth 集成,能够以编程方式创建播放列表,并探索了时间范围和音频特征。这是一个坚实的基础——但你可以在此之上构建更多功能。

可探索的想法

  • 每月快照 – 每月将你的前 50 首歌曲保存到数据库中。几个月后,你将拥有一部可以查询的音乐日记,例如:“我去年三月在听什么?”
  • 基于数据库的被遗忘佳作 – 上面使用的集合减法方法适用于实时 API 数据。若使用 SQLite 数据库累计每月快照,你将拥有更丰富的历史记录可供挖掘。
  • 演变分析 – 跟踪你的音乐品味随时间的变化。计算更替率,发现流派转变,查看夏季播放列表是否更为活泼。
  • 带评分的情绪播放列表生成器 – 与其使用严格的通过/失败过滤,不如为每首曲目打分以匹配情绪画像,并挑选前 N 名匹配项。

这些想法都遵循相同的模式:从 Spotify API 获取数据,存储或与历史数据比较,生成有价值的内容。

本教程改编自 Mastering APIs With Python 第 16 章。如果你想深入学习——SQLite 数据库设计、带重试逻辑的错误处理、使用 mock 的自动化测试,以及涵盖从首次 API 调用到在 AWS 部署的 29 章内容——欢迎查看。

0 浏览
Back to Blog

相关文章

阅读更多 »

我的函数

用户自定义函数 Function:为执行特定任务而编写的代码块 函数的声明定义 - 参数 argument = 形参 parameter - 执行语句 - 返回值 return python def 函数_名称 参数1, 参数2, …: 执行语句 1 执行语句 2 return 参数的种类…