Spotify歌单提取YouTube链接脚本求助:仅获100首+403配额超限
解决Spotify歌单提取不全与YouTube API配额超限问题
一、Spotify歌单仅获取前100首的问题
你的主函数里调用了未定义的fetch_playlist_by_id函数,导致分页逻辑未生效,只能拿到默认的前100首。需要做以下修改:
- 替换调用为你已实现的
fetch_all_tracks函数 - 单独通过Spotify API获取歌单名称
二、YouTube API配额超限403的问题
YouTube Data API免费版每天仅提供10000单位配额,单次搜索请求消耗100单位,所以100次搜索后就会触发403错误。优化方案:
- 让Google API客户端使用缓存,避免重复搜索同一曲目
- 添加异常捕获,处理配额超限情况
- 本地持久化缓存结果,重启脚本也能复用之前的搜索记录
修改后的完整代码
import spotipy from spotipy.oauth2 import SpotifyClientCredentials from googleapiclient.discovery import build import requests_cache from requests_cache.contrib.cache_requests import CacheRequests import time import json from pathlib import Path # 启用requests缓存,同时让googleapiclient使用缓存 requests_cache.install_cache('youtube_cache', expire_after=86400) # 缓存有效期设为1天 CacheRequests.install_cache('google_api_cache', expire_after=86400) # Spotify API credentials (hidden) SPOTIFY_CLIENT_ID = 'XXX' SPOTIFY_CLIENT_SECRET = 'XXX' # YouTube API key (hidden) YOUTUBE_API_KEY = 'XXX' # Spotify playlist ID (hidden) SPOTIFY_PLAYLIST_ID = 'XXX' # 本地缓存文件,存储已搜索过的曲目结果 CACHE_FILE = Path('youtube_search_cache.json') # 加载本地缓存 def load_local_cache(): if CACHE_FILE.exists(): with open(CACHE_FILE, 'r', encoding='utf-8') as f: return json.load(f) return {} # 保存到本地缓存 def save_local_cache(cache_data): with open(CACHE_FILE, 'w', encoding='utf-8') as f: json.dump(cache_data, f, indent=2) # Spotify authentication sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id=SPOTIFY_CLIENT_ID, client_secret=SPOTIFY_CLIENT_SECRET)) # YouTube API youtube = build('youtube', 'v3', developerKey=YOUTUBE_API_KEY) # Fetch all tracks from a playlist, handling pagination def fetch_all_tracks(sp, playlist_id): all_tracks = [] offset = 0 limit = 100 # Spotify API单次最大返回100条 while True: response = sp.playlist_tracks(playlist_id, offset=offset, limit=limit) tracks = response['items'] if not tracks: break all_tracks.extend(tracks) offset += len(tracks) # 计算剩余条目,避免不必要的请求 remaining = response['total'] - offset if remaining <= 0: break limit = min(100, remaining) return all_tracks # 获取歌单名称 def get_playlist_name(sp, playlist_id): playlist = sp.playlist(playlist_id) return playlist['name'] # Extract track information def extract_track_info(track): track_info = { 'name': track['track']['name'], 'artists': [artist['name'] for artist in track['track']['artists']] } return track_info # Search for the respective track on YouTube using API key with rate limiting def search_on_youtube(api_key, query, local_cache): # 先检查本地缓存 cache_key = query.lower().strip() if cache_key in local_cache: return local_cache[cache_key] time.sleep(1) # 限速,避免触发API频率限制 try: request = youtube.search().list(q=query, part='snippet', type='video', maxResults=1) response = request.execute() if not response['items']: print(f"未找到匹配视频: {query}") return None video_id = response['items'][0]['id']['videoId'] # 更新本地缓存 local_cache[cache_key] = video_id save_local_cache(local_cache) return video_id except Exception as e: if 'quotaExceeded' in str(e): print("YouTube API配额已用尽,请明天再试或申请更高配额。") raise SystemExit(1) else: print(f"搜索出错: {str(e)}") return None # Add video to YouTube Music (manual step) def add_to_youtube_music(video_id): if video_id: print(f"添加到YouTube Music: https://www.youtube.com/watch?v={video_id}") else: print("无有效视频链接可添加") print("手动将该视频添加到你的YouTube Music库。") # Transfer playlist to YouTube Music def transfer_to_youtube_music(api_key, playlist_name, tracks): local_cache = load_local_cache() for idx, track in enumerate(tracks, 1): query = f"{track['name']} {', '.join(track['artists'])}" print(f"正在处理第 {idx}/{len(tracks)} 首: {query}") video_id = search_on_youtube(api_key, query, local_cache) add_to_youtube_music(video_id) if __name__ == '__main__': # Fetch playlist from Spotify playlist_name = get_playlist_name(sp, SPOTIFY_PLAYLIST_ID) raw_tracks = fetch_all_tracks(sp, SPOTIFY_PLAYLIST_ID) # 提取曲目信息 tracks = [extract_track_info(track) for track in raw_tracks] print(f"成功获取Spotify歌单 '{playlist_name}' 的 {len(tracks)} 首歌曲") # Transfer playlist to YouTube Music transfer_to_youtube_music(YOUTUBE_API_KEY, playlist_name, tracks) print(f"已完成歌单 '{playlist_name}' 的处理")
关键修改说明
- 修复Spotify分页问题:替换未定义的函数调用,新增
get_playlist_name获取歌单名称,正确执行分页逻辑提取所有曲目 - 强化缓存机制:
- 延长缓存有效期到1天
- 添加本地JSON缓存文件,持久化存储搜索结果,重启脚本无需重复搜索
- 让Google API客户端使用缓存,减少API调用次数
- 配额错误处理:捕获配额超限异常,提示用户并终止程序
- 进度提示:添加处理进度显示,方便跟踪执行状态
- 鲁棒性优化:处理搜索不到视频的情况,避免程序崩溃
内容的提问来源于stack exchange,提问作者Yusei
相关产品推荐
相关产品推荐

