使用Flask与Spotipy开发Spotify推荐系统时遭遇SpotifyException 403错误的排查求助
使用Flask与Spotipy开发Spotify推荐系统时遭遇SpotifyException 403错误的排查求助
大家好,我正在用Flask和Spotipy开发一个Spotify推荐系统,核心功能是根据用户在HTML表单输入的艺术家生成20首推荐歌曲。一周前代码还能完美运行,没报任何错误,但这几天继续开发的时候,突然开始触发SpotifyException,怎么都绕不过去——关键是这段时间我完全没修改过相关代码啊!
错误信息详情
- 浏览器中显示的错误:
spotipy.exceptions.SpotifyException: http status: 403, code:-1 - https://api.spotify.com/v1/audio-features/?ids=1BxfuPKGuaTgP7aM0Bbdwr,2OzhQlSqBEmt7hmkYxfT6m,4q5YezDOIPcoLr8R81x9qy,3hUxzQpSfdDqwM3ZTFQY0K,4R2kfaDFhslZEMJqAFNpdd,1dGr1c8CrMLDpV6mPbImSI,1R0a2iXumgCiFb7HEZ7gUE,0V3wPSX9ygBnCm8psDIegu,1u8c2t2Cy7UBoG4ArRcF5g,0W0iAC1VGlB82PI6elxFYf: None, reason: None
- 控制台输出的错误:
HTTP Error for GET to https://api.spotify.com/v1/audio-features/?ids=1BxfuPKGuaTgP7aM0Bbdwr,2OzhQlSqBEmt7hmkYxfT6m,4q5YezDOIPcoLr8R81x9qy,3hUxzQpSfdDqwM3ZTFQY0K,4R2kfaDFhslZEMJqAFNpdd,1dGr1c8CrMLDpV6mPbImSI,1R0a2iXumgCiFb7HEZ7gUE,0V3wPSX9ygBnCm8psDIegu,1u8c2t2Cy7UBoG4ArRcF5g,0W0iAC1VGlB82PI6elxFYf with Params: {} returned 403 due to None
- 直接点击API调用链接返回的结果:
{ "error": { "status": 401, "message": "No token provided" } }
相关代码片段
app.py中的初始化与认证相关代码
app = Flask(__name__) app.jinja_env.globals.update(zip=zip) app.secret_key = 'secret-key' CLIENT_ID = 'client-id' CLIENT_SECRET = 'client-secret' REDIRECT_URI = 'http://localhost:5000/callback' AUTH_URL = 'https://accounts.spotify.com/authorize' TOKEN_URL = 'https://accounts.spotify.com/api/token' API_BASE_URL = 'https://api.spotify.com/v1/' sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id=CLIENT_ID, client_secret=CLIENT_SECRET)) ... @app.route('/login') def login(): scope = 'user-read-private user-read-email user-library-read user-top-read' params = { 'client_id': CLIENT_ID, 'response_type': 'code', 'scope': scope, 'redirect_uri': REDIRECT_URI, 'show_dialog': True # set to False after testing } auth_url = f"{AUTH_URL}?{urllib.parse.urlencode(params)}" return redirect(auth_url) @app.route('/callback') def callback(): if 'error' in request.args: return jsonify({"error": request.args['error']}) # Login successful if 'code' in request.args: # Build request body with data to be sent to Spotify to get access token req_body = { 'code': request.args['code'], 'grant_type': 'authorization_code', 'redirect_uri': REDIRECT_URI, 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET } response = requests.post(TOKEN_URL, data=req_body) token_info = response.json() session['access_token'] = token_info['access_token'] session['refresh_token'] = token_info['refresh_token'] session['expires_at'] = datetime.now().timestamp() + token_info['expires_in'] # Access token only lasts for 1 day # Fetch user profile user_profile = requests.get(API_BASE_URL + 'me', headers={'Authorization': f"Bearer {token_info['access_token']}"}) user_info = user_profile.json() # Store additional user profile data in session session['user_name'] = user_info['display_name'] session['user_id'] = user_info['id'] session['user_email'] = user_info['email'] session['user_uri'] = user_info['uri'] session['user_link'] = user_info['external_urls']['spotify'] session['user_image'] = user_info['images'][0]['url'] if user_info.get('images') else None return redirect(url_for('home')) @app.route('/refresh-token') def refresh_token(): # Check if refresh token is NOT in the session, redirect to login if 'refresh_token' not in session: return redirect(url_for('login')) # If access token is expired, make a request to get a fresh one if datetime.now().timestamp() > session['expires_at']: req_body = { 'grant_type': 'refresh_token', 'refresh_token': session['refresh_token'], 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET } response = requests.post(TOKEN_URL, data=req_body) new_token_info = response.json() # Override the access token we had before session['access_token'] = new_token_info['access_token'] session['expires_at'] = datetime.now().timestamp() + new_token_info['expires_in'] return redirect(url_for('recommend'))
推荐功能的核心路由代码
@app.route('/new_user_recommendations', methods=['POST']) def new_user_recommendations(): # Ensure the user is logged in if 'access_token' not in session: return jsonify({"error": "Please log in to use this feature"}), 401 # Retrieve artist names from the form artist_names = [request.form.get(f'artist{i}') for i in range(1, 4)] # Key mapping for human-readable keys key_mapping = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] # Validate and fetch artist IDs seed_artists = [] input_audio_features = [] for artist_name in artist_names: if not artist_name: return jsonify({"error": f"Missing artist name for input {artist_names.index(artist_name) + 1}"}), 400 search_result = sp.search(f'artist:"{artist_name}"', type='artist', limit=1) if search_result['artists']['items']: artist_id = search_result['artists']['items'][0]['id'] seed_artists.append(artist_id) # Fetch top tracks and audio features for the artist top_tracks = sp.artist_top_tracks(artist_id)['tracks'] track_ids = [track['id'] for track in top_tracks] audio_features = sp.audio_features(track_ids) # Average the features for visualization normalized_features = { 'tempo': np.mean([feat['tempo'] for feat in audio_features if feat]), 'energy': np.mean([feat['energy'] for feat in audio_features if feat]), 'danceability': np.mean([feat['danceability'] for feat in audio_features if feat]), 'valence': np.mean([feat['valence'] for feat in audio_features if feat]) } input_audio_features.append(normalized_features) else: return jsonify({"error": f"Artist '{artist_name}' not found"}), 404 # Validate that we have enough artists if len(seed_artists) < 1: return jsonify({"error": "No valid artists found for recommendations"}), 400 # Construct the API request headers = { 'Authorization': f"Bearer {session['access_token']}" } params = { 'seed_artists': ','.join(seed_artists), 'limit': 20 } # Add dynamic target features as needed, e.g., tempo, danceability # params['target_tempo'] = 120 url = f'https://api.spotify.com/v1/recommendations?{urllib.parse.urlencode(params)}' try: response = requests.get(url, headers=headers) response.raise_for_status() recommendations = response.json() # Process recommendations recommended_tracks = [] for track in recommendations.get('tracks', []): # Fetch the genres for each artist in the track track_genres = [] for artist in track['artists']: artist_id = artist['id'] artist_info = sp.artist(artist_id) artist_genres = artist_info.get('genres', []) track_genres.extend(artist_genres) recommended_tracks.append({ 'track_name': track['name'], 'artists': ', '.join(artist['name'] for artist in track['artists']), 'album_name': track['album']['name'], 'popularity': track['popularity'], 'preview_url': track['preview_url'], 'genres': track_genres, # Add genres to the track 'id': track.get('id') }) # Fetch audio features for recommended tracks recommendation_ids = [track['id'] for track in recommended_tracks if track['id']] rec_audio_features = sp.audio_features(recommendation_ids) recommended_audio_features = [ { 'tempo': round(feat['tempo']), 'key': key_mapping[feat['key']], 'energy': feat['energy'], 'danceability': feat['danceability'], 'valence': feat['valence'] } for feat in rec_audio_features if feat ] if not recommended_tracks: return jsonify({"error": "No recommendations found. Try with different artists"}), 404 # Render the template return render_template( 'new_user.html', recommendations=recommended_tracks, input_features=input_audio_features, recommendation_features=recommended_audio_features ) except requests.exceptions.RequestException as e: return jsonify({"error": f"Error fetching recommendations: {str(e)}"}), 500
报错位置
根据Traceback,错误出现在new_user_recommendations函数的这一行(对应文件行号358):
audio_features = sp.audio_features(track_ids)
已尝试的解决方法
我已经试过删除原来的Spotify开发者应用,新建了一个并替换了Client ID和Client Secret,但问题依然没有解决。
实在搞不清问题根源在哪了,有没有大佬能帮忙分析下原因或者给点排查方向?
备注:内容来源于stack exchange,提问作者Joely
相关产品推荐
相关产品推荐

