简介面向中秋、国庆、春节等节假日购票难的Python自动化脚本可在电脑端模拟12306购票流程帮助用户自动监控余票、快速提交订单省去手动刷新与排队等待。资源为RAR格式压缩包共8个文件主要包含12306.py主程序、IntelliJ IDEA项目配置文件.iml、xml以及.gitignore等辅助文件整体仅6KB轻量且便于部署其中xml文件多为IDE工作区与模块信息适合直接导入PyCharm/IntelliJ IDEA运行调试。已有2159人学习/下载说明该脚本受到不少抢票用户与学习者的关注。压缩包内项目结构清晰主脚本与配置分离适合有一定Python基础的用户直接运行或二次开发同时也可作为接口模拟、自动化操作、爬虫脚本的学习样例帮助理解购票类网站的常见交互流程。需要注意使用时应遵守12306平台规则合理购票勿用于破坏正常购票秩序。 每年春运抢票那几天我都是全家人的技术总监——一边盯着12306的放票时间一边掐着秒表手动刷新。直到有一年我盯着屏幕从12点整刷到12点05分眼睁睁看着一张票都没了才下定决心必须用Python做一个自动抢火车票脚本把手速和运气这两件事彻底交给代码去处理。这个脚本的核心价值很清楚在12306放票的那个瞬间用程序代替人工高频查询余票、自动提交订单、自动处理验证码把抢票响应时间压缩到百毫秒级别。适合正在学Python爬虫或自动化的人来练手也适合每年都要经历春运抢票大战、愿意折腾一点技术的普通人。这篇文章我会把自己实际开发和测试整个过程整理出来包括整体架构怎么设计、关键接口怎么对接、验证码怎么处理以及我踩过的几个坑希望能帮你少走一些弯路。1. 项目背景与整体设计思路1.1 抢票脚本要解决的核心痛点先聊点实际的手动抢票到底慢在哪里一个是看到余票到点下提交按钮之间的时间差这中间你要经历刷新页面、看清车次、勾选乘客、识别验证码、点击确认怎么也要好几秒钟另一个是起售时间的把握12306每个车站的放票时间不一样不是所有的票都在早上8点开抢为了抢一趟车你可能要提前设好闹钟、盯着时间。脚本解决的正是这两个痛点。程序可以在起售时间前就做好所有准备时间一到立刻高频查询一旦发现有票就直接走下单流程全程不需要人工介入。1.2 整体模块划分与技术选型我在做技术选型时最开始其实纠结过两个方向一个是基于Selenium的浏览器自动化方案一个是直接调用12306接口的requests方案。Selenium的好处是模拟真实浏览器操作写起来直观但缺点也很明显启动浏览器实例很重一个页面加载就要好几秒而且频繁操作很容易触发风险控制。相比之下直接用requests模拟HTTP请求单次查询耗时常驻在几十毫秒还要轻量得多所以我最终选择了接口直调方案。整个脚本我拆成了四个独立模块登录与会话保持模块负责扫码登录、维持Cookie会话余票查询模块负责查车次、查座位余量订单提交模块负责选中车次、填写乘客、提交订单定时与重试模块负责在放票时间点高频触发、失败重试每个模块尽量只做一件事这样后面调试的时候不会牵一发而动全身。2. 核心模块拆解与关键接口解析2.1 登录与会话保持为什么我选扫码而不是账号密码登录是整个流程的起点。12306的账号密码登录其实做了两层防护一是密码本身加密参数很多二是登录后大概率会触发滑块验证如果没处理好程序很容易卡在登录这一步。所以我的选择是直接用官方App的扫码登录。流程很简单程序通过接口获取一个登录二维码我用手机上的12306 App扫一下确认登录程序就能拿到完整的Cookie。这个Cookie里包含了后续所有购票请求的鉴权信息有效期通常在24小时左右每次运行脚本前重新扫一次就行。这里有一个细节获取二维码的接口会返回一个UUID前端会不断轮询是否已扫码的接口直到用户确认。脚本里我把这个轮询逻辑写成了一个循环每2秒查一次状态确认登录后立刻把Cookie保存到本地文件这样下次启动脚本时如果Cookie没过期就能直接复用。2.2 余票查询模块站点编码和票额字段解读余票查询是整个脚本里最核心、也最容易出错的一块。12306的查询接口要求所有站点都用统一编码比如上海虹桥对应SHH北京南对应VNP。这些编码对应关系是公开的可以从12306官网的一个叫station_name.js的文件里获取我把它解析后存成了车站代码映射表。查询接口本身是一个GET请求关键参数有四个日期、出发站编码、到达站编码、乘车人类型成人填ADULT。返回的JSON里data.result数组中的每一行是一条车次记录看起来是一长串用|分隔的数据很多人第一次看到会懵。这里我提取几个关键字段告诉你含义以官方数据格式为准第3个字段车次号比如G1234第4个字段出发站编码第5个字段到达站编码第6个字段出发时间第7个字段到达时间第29个字段二等座余票数量数字代表有票空代表无票第30个字段一等座余票数量第31个字段商务座余票数量第33个字段无座余票数量我单独写了一个解析函数把这些字段拆出来和配置里的车次偏好、席别偏好做匹配匹配成功就进入下单流程。2.3 订单提交模块下单不是一步到位的事很多人以为抢票就是查到有票就点购买但12306的下单流程其实是多个接口串联起来的任何一个环节失败都要重来。大致链路是这样的先请求一下下单页面的初始信息拿到一个token调用checkOrderInfo接口提交乘客信息做校验校验通过后会返回一个提交令牌调用getQueueCount接口查询当前车次的排队人数和预估等待时间调用confirmSingleForQueue接口真正确认提交订单这个流程里最容易出问题的是第2步。乘客信息校验接口要求传一个包含乘车人姓名、身份证号、票种等信息的JSON对象而且不同乘车人的顺序不能随便换。如果乘客信息有误接口会返回乘客信息校验失败但不会告诉你是哪个人出了问题需要你自己排查。整个订单提交过程中还有一个绕不开的坎——验证码。12306用的是选图验证码比如请点击下图中所有的水杯。脚本里我先调用接口获取验证码图片然后把图片交给打码平台拿回坐标最后再提交。这个环节比较依赖打码平台的准确率我实测下来普通情况下准确率在70%到80%之间碰上那种要选红绿灯或者红绿灯消防栓的多选题目出错率会明显上升。2.4 定时调度与重试策略抢起售时间的正确姿势如果你抢的不是刷漏票而是起售时间刚放出来的票那定时调度的价值会成倍放大。12306的放票时间精确到秒比如某个站10点放票那么10点00分00秒之前查到的余票永远是0而10点00分00秒之后立刻就有票。所以我在脚本里加了一个守点模式在设定时间前30秒启动查询循环每隔1秒查一次一旦查到余票数量从0变成有票立即触发下单流程。为了尽量避免系统时间的偏差我用的是本机时间实测下来只要本机时间和北京时间误差在1秒以内成功率就很高。重试策略方面我设置了几层保护查询接口失败或超时不立即终止而是尝试3次下单流程中如果checkOrderInfo返回失败等待2秒后重置下单流程重新尝试最多尝试5次而余票查询本身是多线程进行的每个目标车次一个线程互不干扰。3. 实操环境搭建与核心实现流程3.1 环境准备与依赖安装这部分没什么玄学Python 3.8及以上版本就行。主要的第三方库用到了requestsHTTP请求、Pillow验证码图片处理、prettytable控制台输出车次信息方便调试。安装命令就一条pip install requests pillow prettytable我建议在项目目录下用虚拟环境管理依赖避免和系统Python环境里的包冲突。Windows和macOS、Linux都一样先建一个venv再装依赖这是最稳妥的。3.2 项目文件结构与配置说明我的项目结构是这样的12306_ticket/ ├── main.py # 主入口控制整体流程 ├── config.py # 配置文件乘车日期、车站、车次、乘客 ├── station_name.py # 车站编码映射表 ├── session.py # 登录与Cookie管理 ├── query.py # 余票查询与解析 ├── order.py # 订单提交模块 └── tickets.json # 保存Cookie和登录状态config.py里的关键配置我直接放在文件顶部方便每次改# 乘车日期格式务必和这个保持一致 date 2025-01-28 # 出发站和到达站用中文即可程序会自动查编码 from_station 上海 to_station 武汉 # 优先选择的车次按优先级排序程序会从上往下匹配 prefer_trains [G1728, G1724, G1730] # 乘车人列表支持多乘客12306一单最多5人 passengers [{name: 张三, id_card: 身份证号, seat_type: 二等座}]这里要特别提醒站点名称一定要写正规的铁路站点名称比如上海没问题但如果写上海虹桥站反而可能匹配不上因为配置的是站名前缀。如果你不确定可以直接在12306网页版搜索框里输入看它联想出来的站名。3.3 核心流程从查到下单的关键代码实现主流程的伪代码其实挺清晰的这里我挑最重要的余票查询部分展示一下你大概就能理解整个脚本是怎么运作的import requests import json import time def query_left_ticket(date, from_station_code, to_station_code): 查询指定日期、区间内的所有车次余票信息 url https://kyfw.12306.cn/otn/leftTicket/query params { leftTicketDTO.train_date: date, leftTicketDTO.from_station: from_station_code, leftTicketDTO.to_station: to_station_code, purpose_codes: ADULT } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://kyfw.12306.cn/otn/leftTicket/init } resp requests.get(url, paramsparams, headersheaders, cookiessession.cookies, timeout10) data resp.json() if data.get(status) and data[data].get(result): return parse_train_list(data[data][result], data[data][flag]) return []这段代码的核心就是构建查询参数、携带登录后的Cookie发起请求、解析返回的车次列表。实际运行的时候你会发现12306会对高频请求做限制一旦短时间查询次数太多接口会返回当前访问用户过多这时候需要降低查询频率或者换一个查询入口。订单提交部分的代码链路比较长我在这里不贴全量代码讲两个关键点。第一checkOrderInfo接口返回的JSON里有一个字段叫globalRepeatSubmitToken这个token必须在后面的confirmSingleForQueue请求中带上它是一次性的提交成功后立即失效。第二提交订单前还有一个可选的验证码环节如果配置了打码平台的API程序会自动处理如果没配程序会尝试跳过验证码直接提交有一定概率成功但成功率不高。实测下来节假日高峰期基本都会弹验证码。3.4 运行过程的实测记录我拿自己的一次实测来还原一下运行流程启动脚本后终端先弹出一个二维码我用手机App扫码确认登录。然后脚本进入监听状态控制台会输出类似这样的内容[2025-01-27 09:59:30] 距离开售还有30秒 [2025-01-27 09:59:40] 距离开售还有20秒 [2025-01-27 10:00:00] 开始查询... [2025-01-27 10:00:00] 未查询到G1728余票切换下一优先车次 [2025-01-27 10:00:02] G1724 二等座有余票正在提交订单... [2025-01-27 10:00:03] 订单提交成功请在45分钟内完成支付从放票到下单成功整个过程只用了3秒多。如果不考虑验证码因素脚本的下单速度是非常可观的。但要注意下单成功不等于买票成功你必须在45分钟内登录12306完成支付超时订单会自动取消。4. 常见问题与排查技巧实录4.1 常见问题速查表我在开发和实际使用的过程中遇到过不少莫名其妙的问题这里我整理成了一张速查表方便有类似问题的人快速定位。现象可能原因排查方法与解决方案登录后Cookie很快失效未正确保存Cookie文件检查tickets.json是否存在确认会话未被其他设备顶掉查询接口一直返回当前访问用户过多请求频率太高把查询间隔调到2到3秒以上或切换备用查询域名能查到余票但提交订单失败验证码未通过或token已失效检查打码平台是否正常确认globalRepeatSubmitToken是刚获取的提示非法的乘客信息乘客姓名或身份证号有误逐个人工核对注意姓名中的生僻字编码提交后提示排队人数过多车票过于热门这是正常情况脚本会自动重试也可以选择候补购票脚本一直查不到某车次车站编码映射表过期重新解析station_name.js更新编码表4.2 我踩过的三个大坑第一个坑是日期边界问题。我一开始写查询参数时直接把配置里的日期字符串拼上去结果凌晨零点前后跑的时候发现前一天23:59查的时候显示有票刚过零点再查就显示无票。后来检查才发现12306的系统在零点会做数据切换当天日期需要从当天的零点开始算且日期不能传成昨天。所以我的建议是如果你跑的是跨零点监听最好在程序里先比对一下本机日期和配置日期自动校正。第二个坑是打码平台的延迟问题。某次测试中我发现验证码图片下载下来之后交给打码平台平均要3-5秒才能返回识别结果这个时间在放票高峰期太致命了。后来我做了两个优化一是验证码图片提前下载、提前识别在下单流程真正走到验证码环节之前就预取好结果二是打码平台识别失败时不要立即重新识别而是先用上一次的结果尝试提交如果失败再重试。这样能省不少时间。第三个坑比较隐蔽一单最多只能买5张票而且同一天同一车次只能下一单。我之前在测试时配置了6个乘客结果checkOrderInfo接口一直返回乘客数超限报错信息还特别隐晦。这个限制是12306的硬性规则不是脚本能绕过去的大家配置乘客时注意一下。4.3 实操心得与避坑经验关于整个脚本我有几点自己的体会想多说几句。第一脚本不是万能的它最大的价值在于守住起售点这一波。放票后的前5分钟是成功率最高的窗口期越往后越难抢。所以如果你要抢的票是起售票一定要把脚本的守点模式开起来提前30秒就开始轮询。第二别把这个脚本当成黄牛工具来用。我的代码逻辑里故意没有做大批量刷票、囤票的功能日常使用也只建议帮自己和家人抢票。12306对异常访问有非常明显的风控轻则限制查询重则封禁账号得不偿失。而且说实话官方候补购票的优先级其实高于普通抢票你完全可以先提交候补再用脚本守点抢票双保险。第三代码的每个模块都要能单独调试。我最初把登录、查询、下单写在一个大文件里出了问题很难定位。后来拆成独立模块之后哪个环节挂了直接看日志就能定位这个问题排查快多了。写这种偏流程型的脚本模块化真的能省很多事。我自己现在抢票的基本流程是先看好车次和起售时间提前一天把config.py配置好当天提前10分钟启动脚本扫码登录。放票后脚本自动下单我再打开手机App完成支付。整个流程下来基本能做到人不用一直盯着屏幕。如果你也想试着自己写一个我建议先从余票查询模块开始把查询和解析跑通之后再逐步加上订单提交。别一上来就追求全自动化一步步来踩坑的时候也更有数。本文还有配套的精品资源点击获取
