Python requests请求Snapptrip航班接口返回403未授权错误求助
403报错原因及修复方案
核心报错原因
返回403无权限是三类问题共同导致的:
- 请求上下文完全不匹配:目标URL是德黑兰(THR)到基什岛(KIH)2022-07-01的航班页面,但POST请求体里填的目的地是马什哈德(MHD)、出发日期是2022-06-30,
referer请求头也填的是马什哈德航线的旧页面地址。站点服务端会做全链路参数一致性校验,跨航线、跨日期复用旧请求上下文会被直接判定为异常请求,拒绝访问。 - 鉴权凭证失效且绑定资源不匹配:对照提供的浏览器请求截图可以确认,你复制的
x-access-token、x-request-id、x-session-id、x-customer-key以及Cookie里的会话字段,都是之前访问马什哈德航线页面时生成的短时效凭证,有效期通常只有数分钟,且和当时访问的航线资源强绑定,无法用于请求基什岛的航班资源。 - 请求传参写法存在错误:调用
requests.post时,给json参数传入了json.dumps(data)的序列化结果,会导致请求体被二次JSON编码,服务端无法正常解析参数;同时手动写死了content-length: 147请求头,这个值是之前旧请求的请求体长度,参数修改后实际请求体长度已经变化,错误的content-length也会触发服务端拦截规则。
修复步骤
- 统一全链路请求参数
- 修改POST请求体内容,将
destination字段值改为"KIH",departDate字段值改为"2022-07-01",和URL里的航线、日期参数完全一致 - 修改
referer请求头的值为目标基什岛航班页面地址,删除值开头多余的空格,不要保留旧的马什哈德航线地址
- 修改POST请求体内容,将
- 修正requests库传参逻辑
- 删除手动编写的
content-length请求头,requests会自动根据实际请求体计算并填充正确的长度值 - 发POST请求时直接传
json=data即可,不需要额外对data做json.dumps处理;如果要自行序列化参数,就通过data=json.dumps(data)传参,不要和json参数混用
- 删除手动编写的
- 替换硬编码的失效凭证
直接复制浏览器里的固定鉴权头和Cookie复用成功率极低,这类短时效凭证必须动态获取:- 初始化
requests.Session()对象维持会话,首先用GET请求访问目标基什岛航班的前端页面,让服务端自动给当前会话下发合法Cookie - 从GET返回的页面内容、或者页面加载时触发的接口响应中,动态提取当前会话对应的
x-access-token、x-session-id等鉴权字段,填充到请求头后再发POST查询请求 - 注意不要直接把前端页面URL当POST接口地址,要从浏览器网络面板里抓取实际处理航班查询的XHR接口地址作为POST目标
- 初始化
- 若调整后仍被拦截,检查是否存在动态签名参数:这类航旅站点通常会在页面加载时生成短期请求签名,签名和当前查询的航线、日期、会话强绑定,必须从当前访问的页面动态获取,不能复用旧页面的签名值。
修正后的核心请求参考代码:
import requests # 初始化会话自动维持Cookie session = requests.Session() page_url = "https://www.snapptrip.com/flights/THR-tehran/KIH-kish-island?roundTrip=false&adultCount=1&childCount=0&inLapCount=0&date=2022-07-01&cabinType=E" # 从浏览器网络面板抓实际的查询接口地址替换下方值 api_url = "实际航班查询POST接口地址" base_headers = { 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'accept-language': 'en-US,en;q=0.9', 'origin': 'https://www.snapptrip.com', 'referer': page_url } # 先访问页面获取合法会话凭证 session.get(page_url, headers=base_headers) # 此处补充逻辑:从页面响应或者后续接口返回中提取动态鉴权头,加入base_headers # 构造和URL参数完全匹配的请求体 query_data = { "origin": "THR", "destination": "KIH", "roundTrip": False, "passengers": {"adults": 1, "children": 0, "infants": 0}, "cabinType": "E", "departDate": "2022-07-01" } # 发起查询请求,不手动传content-length,不二次序列化JSON resp = session.post(url=api_url, json=query_data, headers=base_headers) print(resp.status_code, resp.text)
内容的提问来源于stack exchange,提问作者Rahman
相关产品推荐
相关产品推荐

