Python Requests 2.27.1分页循环中URL重复追加查询参数问题
问题根因
- 首先明确requests库的既定行为:如果传入
request()方法的url本身携带查询字符串,方法不会清空原有查询参数,会将params字典传入的参数直接追加到现有查询串末尾,不会做去重、覆盖处理。简单验证即可复现该逻辑:执行requests.get('https://httpbin.org/get?page=0', params={'page':1}),最终请求URL为https://httpbin.org/get?page=0&page=1,服务端默认会读取第一个重复参数的值,和你遇到的现象完全一致。 - 你本地针对httpbin的测试代码运行正常,核心原因是测试代码中
url变量从头到尾都是无参数的基础路径,从未被污染,因此每次都是基于干净URL拼接参数,不会出现重复追加。 - 业务代码出现参数持续追加,唯一直接原因是:while循环进入第二轮及之后,传入
session.request()的url变量已经是上一轮请求生成的带查询字符串的完整URL,不再是初始传入的无参数基础路径。
结合你贴的代码逻辑,90%以上概率是你实际运行的代码中存在遗漏的URL赋值逻辑(比如之前写过从响应头、响应体提取next链接的逻辑未清理干净,存在url = response.url、url = result['next']这类赋值),把上一轮的带参URL赋值给了循环内的url变量;剩余低概率原因是Session挂载了自定义请求钩子、认证类、传输适配器,错误复用了上一次的请求对象导致URL被重复拼接参数。
你观察到的“page参数卡在1无法继续推进”现象也和这个逻辑完全吻合:第二轮请求的URL已经携带page=0,即使params里设置page=1,最终URL里会同时存在page=0和page=1,服务端读取第一个page=0返回对应页数据,你的代码读取响应里的page=0,又会把params的page设为1,进入死循环重复追加page=1&size=100参数。
合规解决方案
不要使用url.split('?')[0]这类临时规避方案,按以下步骤修复:
- 先定位污染源:在while循环内调用
session.request()的前一行加调试日志,打印当前传入的url值,确认第二轮开始url是否已携带查询参数,回溯找到给url赋值带参地址的代码段并删除——你的inline分页逻辑完全靠params控制翻页参数,不需要从响应中提取翻页URL。 - 从根源避免URL被污染:进入while循环前,将初始传入的基础URL存为固定变量,同时复制参数副本避免修改外部传入的字典引用,核心修改示例如下:
import json as native_json from urllib.parse import urlparse, parse_qs, urlencode def __request(self, method, url, params=None, data=None, json=None): return_data = [] num_iterations = 1 pagination_type = None # 解析原始URL,固定干净的基础路径,把自带的查询参数合并到请求参数里 parsed_url = urlparse(url) base_url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" # 初始化参数副本,避免修改外部传入的params引用造成副作用 request_params = {} if parsed_url.query: request_params.update(parse_qs(parsed_url.query, keep_blank_values=True)) if params is not None: request_params.update(params) while True: # 永远用干净的base_url发请求,避免URL带参导致参数追加 response = self.session.request(method, base_url, params=request_params, data=data, json=json) self.__check_response_for_error(response) if self.__response_has_no_content(response): return None try: result = response.json() except native_json.decoder.JSONDecodeError: return response.content.decode('utf-8') if num_iterations == 1: pagination_type = self.__pagination_type(response.headers, result) if pagination_type == 'inline': return_data += result['data'] count = result['count'] page = result['page'] size = result['size'] if size * (page + 1) >= count: break else: # 仅修改参数副本,不影响外部传入的原始参数 request_params['page'] = page + 1 request_params['size'] = size elif pagination_type == 'audit': # 原有业务逻辑保持不变,涉及翻页参数修改统一操作request_params pass elif pagination_type == 'report': # 原有业务逻辑保持不变 pass elif pagination_type == 'secmgr': # 原有业务逻辑保持不变 pass else: return_data = result break num_iterations += 1 return return_data - 修复后验证:在调用
session.request前打印base_url和request_params,确认每一轮请求的base_url都是无参数的基础路径,request_params里的page参数按预期递增,最终生成的请求URL不会出现重复参数即可。
内容的提问来源于stack exchange,提问作者thomas
相关产品推荐
相关产品推荐

