使用Requests调用API遇401 Unauthorized,如何自动重新认证?
解决CEASA API 401未授权自动重认证问题
问题原因
你的请求返回401是因为headers中的JSESSIONID Cookie已过期。这个Cookie是服务器用来标识用户会话的,超时或长时间未活动后会失效,必须重新获取有效会话标识才能继续请求。
解决方案
通过代码自动获取新的会话Cookie,并且在请求失败(401)时自动重试。核心思路是:
- 先访问CEASA数据平台首页,获取服务器返回的有效
JSESSIONID - 使用
requests.Session()管理会话,自动维护Cookie - 当API请求返回401时,重新获取会话并重发请求
完整代码实现
import requests import json from bs4 import BeautifulSoup as bs4 import pandas as pd API_URL = "https://dw.ceasa.gov.br/saiku/rest/saiku/api/query/execute" HOME_URL = "https://dw.ceasa.gov.br/" payload = json.dumps({ "name": "0A777C63-EAF6-181A-9FB1-248AFCE90BE1", "queryModel": { "axes": { "FILTER": { "mdx": None, "filters": [], "sortOrder": None, "sortEvaluationLiteral": None, "hierarchizeMode": None, "location": "FILTER", "hierarchies": [], "nonEmpty": False, "aggregators": [] }, "COLUMNS": { "mdx": None, "filters": [], "sortOrder": None, "sortEvaluationLiteral": None, "hierarchizeMode": None, "location": "COLUMNS", "hierarchies": [ { "name": "[04-Produto].[04-Produto]", "levels": { "Produto": { "name": "Produto" } } } ], "nonEmpty": True, "aggregators": [] }, "ROWS": { "mdx": None, "filters": [], "sortOrder": None, "sortEvaluationLiteral": None, "hierarchizeMode": None, "location": "ROWS", "hierarchies": [ { "name": "[06-Ano].[06-Ano]", "levels": { "Ano": { "name": "Ano", "aggregators": [], "selection": { "type": "EXCLUSION", "members": [ { "uniqueName": "[06-Ano].[06-Ano].[2015]", "caption": "2015" }, { "uniqueName": "[06-Ano].[06-Ano].[2016]", "caption": "2016" }, { "uniqueName": "[06-Ano].[06-Ano].[2017]", "caption": "2017" }, { "uniqueName": "[06-Ano].[06-Ano].[2018]", "caption": "2018" }, { "uniqueName": "[06-Ano].[06-Ano].[2019]", "caption": "2019" }, { "uniqueName": "[06-Ano].[06-Ano].[2020]", "caption": "2020" }, { "uniqueName": "[06-Ano].[06-Ano].[2021]", "caption": "2021" }, { "uniqueName": "[06-Ano].[06-Ano].[2022]", "caption": "2022" }, { "uniqueName": "[06-Ano].[06-Ano].[2023]", "caption": "2023" } ] } } } }, { "name": "[07-Mes].[07-Mes]", "levels": { "Mes": { "name": "Mes", "aggregators": [], "selection": { "type": "EXCLUSION", "members": [ { "uniqueName": "[07-Mes].[07-Mes].[1º SEM].[JANEIRO]", "caption": "JANEIRO" }, { "uniqueName": "[07-Mes].[07-Mes].[1º SEM].[FEVEREIRO]", "caption": "FEVEREIRO" }, { "uniqueName": "[07-Mes].[07-Mes].[1º SEM].[MARÇO]", "caption": "MARÇO" }, { "uniqueName": "[07-Mes].[07-Mes].[1º SEM].[ABRIL]", "caption": "ABRIL" }, { "uniqueName": "[07-Mes].[07-Mes].[1º SEM].[MAIO]", "caption": "MAIO" }, { "uniqueName": "[07-Mes].[07-Mes].[1º SEM].[JUNHO]", "caption": "JUNHO" }, { "uniqueName": "[07-Mes].[07-Mes].[2º SEM].[JULHO]", "caption": "JULHO" } ] } } } }, { "name": "[08-Dia].[08-Dia]", "levels": { "Dia Mes": { "name": "Dia Mes" } } } ], "nonEmpty": True, "aggregators": [] } }, "visualTotals": False, "visualTotalsPattern": None, "lowestLevelsOnly": False, "details": { "axis": "COLUMNS", "location": "BOTTOM", "measures": [] }, "calculatedMeasures": [] }, "queryType": "OLAP", "type": "QUERYMODEL", "cube": { "uniqueName": "[Prohort].[Prohort].[Prohort].[PROHORT - PRECO DIARIO]", "name": "PROHORT - PRECO DIARIO", "connection": "Prohort", "catalog": "Prohort", "schema": "Prohort", "caption": None, "visible": False }, "mdx": None, "parameters": {}, "plugins": {}, "properties": { "saiku.olap.query.automatic_execution": False, "saiku.olap.query.nonempty": True, "saiku.olap.query.nonempty.rows": True, "saiku.olap.query.nonempty.columns": True, "saiku.ui.render.mode": "table", "saiku.olap.query.filter": True, "saiku.olap.result.formatter": "flattened", "org.saiku.query.explain": True, "org.saiku.connection.scenario": False, "saiku.olap.query.drillthrough": True }, "metadata": {} }) def get_session(): """创建新会话并获取有效Cookie""" session = requests.Session() # 访问首页获取JSESSIONID session.get(HOME_URL, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36', 'Accept-Language': 'pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7' }) return session def execute_query(session): """执行API请求,返回响应""" headers = { 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Accept-Language': 'pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7', 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Origin': 'https://dw.ceasa.gov.br', 'Referer': 'https://dw.ceasa.gov.br/', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest', 'sec-ch-ua': '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"' } response = session.post(API_URL, headers=headers, data=payload) return response # 主逻辑 session = get_session() response = execute_query(session) # 如果返回401,重新获取会话并重试 if response.status_code == 401: print("会话过期,重新获取会话...") session = get_session() response = execute_query(session) print(response.status_code) # 后续可以处理响应内容,比如解析为DataFrame # if response.status_code == 200: # data = response.json() # df = pd.DataFrame(data['resultset'])
代码说明
get_session()函数:创建新的requests.Session对象,访问CEASA首页获取服务器颁发的新JSESSIONID,会话会自动保存这个Cookie。execute_query()函数:使用传入的会话执行API请求,无需手动指定Cookie,会话会自动携带。- 主逻辑:先尝试请求,若返回401则重新获取会话并重试,确保请求始终使用有效会话。
内容的提问来源于stack exchange,提问作者Ramiro
相关产品推荐
相关产品推荐

