Python分页获取API全量数据时出现签名不匹配错误求助
问题排查:API分页请求签名不匹配错误
你遇到的签名不匹配问题,主要是因为签名生成逻辑、时间戳处理和分页循环的几个错误导致的。我先帮你拆解问题,再给出修正后的代码。
首先看你的代码和输出,能发现几个明显的问题:
核心错误原因
签名计算包含了旧的Signature字段
从你打印的concatenated内容可以看到,每次生成签名时,参数里都包含了上一次请求的Signature值。而API的签名逻辑肯定是要求只对除Signature外的其他参数进行哈希计算,把旧签名加进去会导致计算出来的新签名和API预期的完全不符。时间戳没有随请求更新
你只在函数开头生成了一次Timestamp,整个分页循环都用同一个时间戳。很多API会校验请求时间戳的有效性(比如必须是当前时间的几分钟内),重复使用旧时间戳很容易触发签名验证失败。分页逻辑严重错误
- 你的代码在第一次请求后就直接
return page_json,根本不会执行后续的分页请求; - 循环内的try-except块重复发送请求,逻辑混乱,也没有判断什么时候停止分页(比如返回的数据不足500条时应该终止循环)。
- 你的代码在第一次请求后就直接
修正后的完整代码
import datetime import urllib.parse import requests import time from hmac import HMAC from hashlib import sha256 def get_data(userid, api_key, action, pagination=True): endpoint = 'https://example.com' all_data = [] # 用来存储所有分页返回的数据 # 定义基础参数,这些参数在分页过程中不会变化 base_params = { 'UserID': userid, 'Version': '1.0', 'Action': action, 'Format': 'JSON' } if not pagination: # 不分页的情况,直接请求默认数量 params = base_params.copy() # 生成当前请求的时间戳 timestamp = datetime.datetime.now().replace(microsecond=0).isoformat() + '+08:00' params['Timestamp'] = timestamp # 生成签名:只对除Signature外的参数排序拼接 sorted_params = sorted(params.items()) concatenated = urllib.parse.urlencode(sorted_params) params['Signature'] = HMAC(api_key.encode('utf-8'), concatenated.encode('utf-8'), sha256).hexdigest() try: response = requests.get(endpoint, params=params) response.raise_for_status() # 触发HTTP错误,比如4xx/5xx return response.json() except requests.exceptions.ConnectionError: print("连接被拒绝!5秒后重试...") time.sleep(5) # 重试一次 response = requests.get(endpoint, params=params) response.raise_for_status() return response.json() # 分页获取数据的逻辑 page = 0 limit = 500 while True: # 每次请求都复制基础参数,避免污染原参数 params = base_params.copy() # 每次请求都生成新的时间戳 timestamp = datetime.datetime.now().replace(microsecond=0).isoformat() + '+08:00' params['Timestamp'] = timestamp params['Limit'] = limit params['Offset'] = limit * page # 生成签名:注意这里只排序拼接除Signature外的参数 sorted_params = sorted(params.items()) concatenated = urllib.parse.urlencode(sorted_params) params['Signature'] = HMAC(api_key.encode('utf-8'), concatenated.encode('utf-8'), sha256).hexdigest() try: response = requests.get(endpoint, params=params) response.raise_for_status() page_json = response.json() # 假设数据存储在Data字段,根据你的API实际结构调整 current_page_data = page_json.get('Data', []) if not current_page_data: # 没有数据了,终止循环 break all_data.extend(current_page_data) # 如果当前页返回的数据数量小于limit,说明是最后一页 if len(current_page_data) < limit: break page += 1 time.sleep(1) # 加个小延迟,避免请求太频繁触发API限流 except requests.exceptions.ConnectionError: print("连接被拒绝!5秒后重试当前页...") time.sleep(5) continue # 重试当前页,不递增page except requests.exceptions.HTTPError as e: print(f"请求出错:{e}") break # 返回包含所有数据的结构,和API返回格式保持一致 return {'Data': all_data}
关键修改点说明
签名生成逻辑修复
每次生成签名前,只对Timestamp、Offset、Limit等必要参数排序拼接,完全排除Signature字段,确保和API的签名计算规则一致。动态更新时间戳
在每次分页请求时都重新生成当前时间的时间戳,避免因时间戳过期或重复导致的验证失败。正确的分页循环逻辑
- 用
all_data收集所有分页的数据,直到返回数据为空或者数量不足Limit时停止循环; - 移除了冗余的重复请求代码,优化了错误处理逻辑;
- 添加了请求间隔,防止触发API的限流机制。
- 用
参数隔离
使用base_params存储不变的参数,每次请求都复制一份,避免参数被污染(比如上次的Signature残留)。
内容的提问来源于stack exchange,提问作者AnalyticsPy
相关产品推荐
相关产品推荐

