百趣云 百趣云的博客

12306 购票接口参数与 cookie 体系分析

12306 的防护哲学:流程即城墙

12306 是"重 cookie、轻签名"的极致代表:查询和下单接口没有任何客户端签名,但 cookie 体系和请求编排层层嵌套,缺一个环节就返回"网络可能存在问题,请您稍后重试"——这句提示 90% 的情况不是网络问题,是你的流程不对。

核心 cookie 地图

cookie作用种下时机
JSESSIONID会话标识进站即种
BIGipServerotn负载均衡,决定落到哪台后端进站即种
_jc_save_fromStation出发站(编码格式:北京,BJP查询页行为
_jc_save_toStation到达站查询页行为
_jc_save_fromDate / _jc_save_toDate乘车/查询日期查询页行为
tk登录票据登录成功
RAIL_EXPIRATION / RAIL_DEVICEID设备指纹指纹 JS 生成

关键认知:leftTicket/query 接口会校验 _jc_save_* 系列 cookie 与查询参数的一致性。不先走查询页流程种 cookie 就直接 POST 查询,必挂。

正确的请求编排

import requests, re, json, time

session = requests.Session()
session.headers['User-Agent'] = 'Mozilla/5.0 ...'

# 1. 进站初始化
session.get('https://kyfw.12306.cn/otn/leftTicket/init')

# 2. 模拟查询页行为,设置上下文 cookie(注意格式:站名,电报码)
session.cookies.set('_jc_save_fromStation', '北京,BJP', domain='kyfw.12306.cn')
session.cookies.set('_jc_save_toStation', '上海,SHH', domain='kyfw.12306.cn')
session.cookies.set('_jc_save_fromDate', '2026-09-01', domain='kyfw.12306.cn')
session.cookies.set('_jc_save_toDate', '2026-09-01', domain='kyfw.12306.cn')

# 3. 查询余票(queryG/queryA/query 端点随版本轮换,都试试)
resp = session.get('https://kyfw.12306.cn/otn/leftTicket/queryG', params={
    'leftTicketDTO.train_date': '2026-09-01',
    'leftTicketDTO.from_station': 'BJP',
    'leftTicketDTO.to_station': 'SHH',
    'purpose_codes': 'ADULT',
})

下单链路:token 藏在页面里

下单是多步流程,每步的凭证从上一步的响应里取:

  1. submitOrderRequest:用查询结果里的 secretStr(urldecode 后按 | 拆分解出车次信息)
  2. initDc 页面:响应 HTML 里藏着两个关键变量——var globalRepeatSubmitToken = 'xxx'(防重复提交令牌)和 ticketInfoForPassengerForm(座位/票价数据),正则提取
  3. getPassengerDTOs:拿乘客列表,带 REPEAT_SUBMIT_TOKEN
  4. checkOrderInfo:提交选中的乘客
  5. confirmSingleForQueue:最终确认,核心是 passengerTicketStroldPassengerStr 两个参数的拼装:
# passengerTicketStr: 座位类型,0,票种,姓名,证件类型,证件号,手机号,N
passenger_ticket_str = f"{seat_type},0,1,{name},1,{id_no},{phone},N"
# oldPassengerStr: 姓名,证件类型,证件号,1_
old_passenger_str = f"{name},1,{id_no},1_"
  1. 轮询 queryOrderWaitTime 拿排队结果

精髓:设备指纹与实战坑

  1. RAIL_DEVICEID 是指纹 JS 生成的。包含 canvas、ua、屏幕等信息,登录和高频下单时校验。协议模拟从真实浏览器采集后固定,和 ua 成套。
  2. 登录滑块。账号密码登录有滑块验证,识别+轨迹模拟是标配;扫码登录(12306 App)可以绕过,但 App 端是另一套协议。
  3. "网络可能存在问题"= 流程错误。看到这个提示,按顺序排查:cookie 缺没缺 → 编排顺序对不对 → REPEAT_SUBMIT_TOKEN 是不是用的最新值(每次 initDc 都变)。
  4. 查询接口端点轮换queryqueryAqueryGqueryZ 历史上轮着用,当前有效端点以 init 页面里的 CLeftTicketUrl 变量为准——动态提取,别写死。
  5. 车站电报码表station_name.js 这个静态文件里有全量站名-电报码映射,本地缓存一份定期更新。

总结

  1. 12306 无签名,防护全在 cookie 体系和请求编排
  2. _jc_save_* 上下文 cookie 与查询参数必须一致
  3. 下单链路的 token 在 initDc 页面 HTML 里,每轮都变
  4. "网络可能存在问题"是流程错误的统一话术,按清单排查
By 百趣云 阅读量:1 On