You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spotify API访问令牌过期:如何重置以维持Flask应用运行

问题描述

我在PythonAnywhere.com上部署了一个Python/Flask业余Web应用,用来实现Spotify原生不支持的功能。手动通过PythonAnywhere的“Web”标签页重启应用后,应用运行正常,但一段时间后,前端页面发起API调用时会返回“Internal Server Error”。

排查发现,约1小时后调用Spotify API会返回401错误,原因是Spotify访问令牌仅有效期1小时。我曾推测PythonAnywhere仅在重启应用时执行一次令牌刷新逻辑,因此令牌60分钟后过期。于是我尝试将获取访问令牌的逻辑封装为函数,在各API调用函数中调用,但未解决问题。我目前的猜想是Web服务器仅在重启时获取一次令牌,但不确定如何排查。

请问该猜想是否合理?或有其他排查方向,无需手动重启服务器即可维持应用正常运行?


相关代码

常量设置

AUTH_URL = 'https://accounts.spotify.com/api/token'
BASE_URL = 'https://api.spotify.com/v1/'

获取访问令牌并返回请求头的代码

auth_response = requests.post(AUTH_URL, {
    'grant_type': 'client_credentials',
    'client_id': CLIENT_ID,
    'client_secret': CLIENT_SECRET,
})


def setHeaders():
    try:
        auth_response.raise_for_status()
    except requests.exceptions.HTTPError as e:
        print("!!!Error setting auth_response. System detail:")
        print(e)
        raise

    # 转换为JSON格式
    auth_response_data = auth_response.json()

    # 保存访问令牌
    access_token = auth_response_data['access_token']

    headers = {
        'Authorization': 'Bearer {token}'.format(token=access_token)
    }

    return headers

调用Spotify API获取用户所有播放列表的函数

# API GET地址: https://api.spotify.com/v1/users/{user_id}/playlists
def getRawPlaylistMetadata(username,offset_value=0):

    headers = setHeaders()

    r_list = []
    result_limit = 50
    # print(offset_value)

    params = {'offset': offset_value,
              'limit': result_limit}

    r = requests.get(BASE_URL + 'users/' + username + '/playlists', 
        headers=headers,params=params)

    response_code = str(r.status_code)

    sys.stderr.write("\nr response code=" + response_code + "\n\n")

    r = r.json()

    try:
        r_list = r['items']
    except KeyError:
        print("获取Spotify用户所有播放列表元数据失败。")

    if len(r_list) < result_limit:
        # print(len(r_list))
        return r_list

    return r_list + getRawPlaylistMetadata(username,offset_value + len(r_list))

解答

你的猜想部分正确,但核心问题出在代码逻辑上,而非PythonAnywhere的服务器机制:

  1. 问题根源:
    你在函数外部提前执行了auth_response = requests.post(...),这行代码只会在应用启动(重启)时运行一次,获取的令牌自然1小时后过期。而setHeaders()函数只是反复使用这个已经过期的auth_response对象,并没有重新请求新的令牌——这就是为什么封装成函数也没用的原因。

  2. 验证方法:

    • 在setHeaders()函数内添加日志,打印当前access_token的获取时间或过期时间(Spotify返回的expires_in字段是秒数,默认3600),就能确认是否每次调用都在刷新令牌。
    • 查看PythonAnywhere的服务器日志,检查调用Spotify API时返回的401错误是否伴随令牌过期的提示。
  3. 解决方案:
    修改setHeaders()函数,将令牌请求逻辑移到函数内部,确保每次调用都重新获取(或判断是否需要刷新)令牌:

    def setHeaders():
        try:
            # 将请求令牌的逻辑移到函数内部,每次调用都重新请求
            auth_response = requests.post(AUTH_URL, {
                'grant_type': 'client_credentials',
                'client_id': CLIENT_ID,
                'client_secret': CLIENT_SECRET,
            })
            auth_response.raise_for_status()
        except requests.exceptions.HTTPError as e:
            print("!!!Error setting auth_response. System detail:")
            print(e)
            raise
    
        auth_response_data = auth_response.json()
        access_token = auth_response_data['access_token']
    
        headers = {
            'Authorization': 'Bearer {token}'.format(token=access_token)
        }
    
        return headers
    

    进一步优化:可以缓存令牌和过期时间,避免每次API调用都请求令牌(比如记录expires_at = time.time() + auth_response_data['expires_in'],每次调用前检查当前时间是否超过expires_at,再决定是否刷新)。

  4. 其他排查方向:

    • 检查PythonAnywhere的应用进程是否被意外重启或休眠(免费版有休眠机制,长时间无访问会暂停进程,但重启后令牌会重新获取,和你的情况不符)。
    • 确认CLIENT_ID和CLIENT_SECRET是否正确,有没有在环境变量中配置错误(重启时能正常运行,说明配置没问题)。

内容的提问来源于stack exchange,提问作者slimPickens142

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:30:42