资讯详情

Java抢票助手源码拆解:自动登录、查询与订单提交实战

📅 2026/10/30 2:54:41 | 华诺云谱 👁 阅读
Java抢票助手源码拆解:自动登录、查询与订单提交实战
简介这份资源面向熟悉 Java、希望在春运等购票高峰自动抢购 12306 火车票的程序员提供一套可运行的自动购票程序及配套说明。程序支持自动登录与重试、自动查询刷新、指定车次与动车/卧铺优先、代理配置及验证码识别并兼容 FirefoxScriptish 与 Chrome 环境帮助无法时刻盯票的用户提升购票成功率。压缩包共 1 个 doc 文件约 467KB以图文教程形式记录程序功能、配置项与使用流程便于对照操作。目前已有 704 人学习浏览。读者可从中了解自动填单、重试机制、config.properties 参数设置与验证码处理思路也能借鉴其网页解析与自动化实现方法适合作为 Java 网络爬虫与自动化实践的学习案例。1. 抢票这件事程序员有自己的解法春运放票那天我盯着屏幕刷了四十分钟验证码换了二十多张最后连站票都没了。旁边工位的同事却慢悠悠地喝着茶说他写了个 Java 小程序挂着自动刷抢到票才弹窗提醒他。这就是我拆这个 GOHOME 抢票助手的起因——它不是什么高深的东西但把「自动登录、自动重试、自动刷新查询、自动提交订单」这条链路串起来了用 Java 写双击start.bat就能跑。适合谁适合想搞明白自动化购票流程怎么落地的人也适合拿它当 HttpUnit、HTML 解析、验证码识别入门案例的 Java 开发者。下面我按「它是什么 → 怎么跑起来 → 核心代码怎么写的 → 怎么改」的顺序把这份源码包拆开讲。2. 项目定位与技术栈一个 Java 桌面端的网页自动化工具2.1 它到底在做什么这个程序本质是一个「带界面的网页机器人」。它不调用任何官方接口而是模拟浏览器行为打开登录页、填表单、提交、解析返回的 HTML、判断有没有票、有票就提交订单。整个流程跑在一个 Java 桌面窗口里用户填好出发站、到达站、日期、车次偏好点开始程序就循环执行「查询 → 判断 → 提交」直到成功或用户手动停。从源码包的结构看它依赖三块东西一是 HTTP 客户端用来发请求、维持会话Cookie二是 HTML 解析器用来从返回页面里抠出车次、余票、验证码图片地址三是验证码识别模块把图片转成字符。原文提到作者用htmlparse爬网页、用颜色像素思路识别验证码这和我们常见的「HttpClient Jsoup 简单图像处理」组合是对得上的。2.2 技术栈拆解层次选型作用运行环境JRE 1.6程序是 Java 编写start.bat里调java -jar网络层HttpUnit / 原生 HttpURLConnection发 GET/POST、维持 Cookie 会话解析层htmlparseHTML 解析库从页面提取车次、余票、表单隐藏字段图像层Java AWT / BufferedImage读取验证码图片像素做识别配置层config.properties代理、指定车次、优先级开关界面层Swing输入框、按钮、日志区、弹窗提示这里有个血泪经验很多人以为抢票程序是「直接调接口」其实早期这类工具全是「模拟浏览器点按钮」因为购票流程里有一堆隐藏字段比如token、leftTicketStr必须从上一个页面带过来漏一个就提交失败。所以它的核心不是算法是「把浏览器该做的事一件不落地重放一遍」。2.3 目录与文件构成解压后大致是这么几块我按功能归类GOHOME/ ├── start.bat # 启动脚本检查并调用 java -jar ├── config.properties # 代理、指定车次等配置 ├── 使用说明.txt # 原始操作指引 ├── lib/ # 依赖 jarhttp 客户端、html 解析等 └── GOHOME.jar # 主程序含界面与业务逻辑start.bat的内容很朴素常见写法是echo off rem 检查 java 是否可用不可用则提示安装 JRE java -version nul 21 if errorlevel 1 ( echo 未检测到 Java 运行环境请先安装 JRE 1.6 以上版本 pause exit /b ) rem 启动主程序 java -jar GOHOME.jar pause这段脚本干两件事先用java -version探一下环境errorlevel 1说明没装或没配 PATH直接提示否则java -jar拉起主类。参数上没什么可调的真正要改的是config.properties。注意pause别删程序异常退出时窗口不会一闪而过方便看报错——我第一次跑就是靠这行看到「找不到主类」的。3. 跑起来从 JRE 环境到第一张票的完整链路3.1 环境准备与启动第一步永远是环境。程序要求 JRE 大于 1.6原文建议直接装 JRE 6 Update 30。现在机器上多半有更高版本java -version能出版本号就行。装完在命令行敲一下确认java -version # 期望输出类似java version 1.8.0_xxx然后双击start.bat。界面初始化完你会看到出发站、到达站、日期、车次、是否自动识别验证码、动车优先、卧铺优先这些输入项。填完点开始程序进入循环。这里有个细节原文说「除登录信息外其他信息均可及时修改并即时生效」意思是循环每一轮都会重新读界面上的值所以你中途改车次不用重启。3.2 配置文件怎么改config.properties是纯文本记事本打开就能编辑。代理和指定车次是两块重点# 代理服务器配置需要走代理时把 userproxy 改为 true userproxyfalse ip127.0.0.1 port8080 # 指定车次如 traincodeD520 # 只是优先预定该车次无票时程序会自动选其他车次 traincode # 是否优先动车、优先硬卧 preferDtrue preferSleepertrue参数说明userproxy是总开关改成true后ip和port才生效traincode留空表示不指定填了就是「优先」而非「只买」——这点原文特意强调过因为很多人以为填了车次就只抢这一趟结果没票干等。preferD、preferSleeper对应界面上的动车优先、卧铺优先逻辑是排序时把符合条件的车次排前面不是过滤掉其他车次。3.3 一次完整抢票的时序把流程按时间线捋一遍方便你对照日志排查启动后读取config.properties初始化 HTTP 客户端和 Cookie 容器。打开登录页若开启「记住密码」从本地存储读出用户名密码自动填入光标定位到验证码框。验证码环节自动识别模式下调图像模块识别失败则重试手动模式弹框等你输入。提交登录若返回「人数过多/网络繁忙」按重试策略等待后重发直到成功。登录成功后进入查询循环按出发站、到达站、日期拼查询请求解析返回的车次列表。对每个车次判断余票命中偏好指定车次/动车/卧铺则进入提交订单流程。提交订单需要带上页面里的隐藏字段成功后弹窗声音提示剩下的支付要人工去订单页完成。提示原文说支付窗口只有 45 分钟程序抢到票只是「占位」真正付钱还得你手动登录网页操作所以抢到后别关程序赶紧去支付。4. 核心模块拆解登录、查询、提交三段代码4.1 会话维持与自动登录抢票程序最怕的是「登录成功但下一个请求掉线」根因是 Cookie 没带全。常见做法是全局维护一个 Cookie 容器每次请求前把已有 Cookie 塞进请求头响应里的Set-Cookie再回写。伪代码结构如下// 全局会话容器保存登录后的 Cookie private MapString, String cookieStore new HashMap(); // 发请求前注入 Cookie private void attachCookies(HttpRequest request) { StringBuilder sb new StringBuilder(); for (Map.EntryString, String e : cookieStore.entrySet()) { sb.append(e.getKey()).append().append(e.getValue()).append(; ); } if (sb.length() 0) { request.setHeader(Cookie, sb.toString()); } } // 响应回来后更新 Cookie private void saveCookies(HttpResponse response) { ListString setCookies response.getHeaders(Set-Cookie); for (String c : setCookies) { String pair c.split(;)[0]; // 只取 keyvalue 部分 String[] kv pair.split(, 2); cookieStore.put(kv[0].trim(), kv[1].trim()); } }逻辑说明attachCookies在每个请求发出前把容器里的键值拼成Cookie头saveCookies从响应的Set-Cookie里剥出keyvalue忽略Path、Expires这些属性对维持会话够用。参数上要注意split(, 2)的2因为 Cookie 值里可能含不限次数会切坏。自动登录就是在登录页表单里预填账号密码再把焦点设到验证码输入框这部分是 Swing 的setText和requestFocus没什么玄学。4.2 查询与余票解析查询请求发出去后返回的是一大段 HTML里面藏着车次表格。解析的关键是定位到表格行再按列取字段。用 htmlparse 的思路大致是// 解析查询结果页提取车次与余票 public ListTrain parseTrains(String html) { ListTrain list new ArrayList(); // 定位车次表格的每一行 ListHtmlElement rows HtmlParser.parse(html).findAll(tr[id^ticket_]); for (HtmlElement row : rows) { Train t new Train(); t.code row.getText(td.train-num); // 车次号 t.from row.getText(td.from-station); // 出发站 t.to row.getText(td.to-station); // 到达站 t.left row.getText(td.left-ticket); // 余票描述 t.secret row.getAttribute(data-secret); // 提交订单要用的隐藏串 list.add(t); } return list; }逻辑说明tr[id^ticket_]是按行 id 前缀筛车次行getText取单元格文本getAttribute取隐藏字段。这里最容易翻车的是secret这类隐藏串——它每次查询都会变提交订单时必须用「最近一次查询」拿到的值缓存旧值会直接报「订单提交失败」。参数上余票字段可能是「有」「无」「12」这种混合文本判断时要先做空值和「无」的过滤别直接Integer.parseInt。4.3 自动重试与订单提交重试机制是这个程序的灵魂。原文说「遇购票人数过多或网络繁忙会自动重试直到成功」实现上就是一个带退避的循环// 带最大次数和间隔的重试封装 public T T retry(CallableT action, int maxTimes, long intervalMs) throws Exception { Exception last null; for (int i 0; i maxTimes; i) { try { T result action.call(); if (result ! null) return result; // 成功即返回 } catch (Exception e) { last e; // 记录最后一次异常 } Thread.sleep(intervalMs); // 固定间隔避免请求过密 } throw last ! null ? last : new RuntimeException(重试次数耗尽); }逻辑说明action是任意可重试的操作登录、查询、提交maxTimes控制上限防止死循环intervalMs是每次失败后的等待。参数怎么调登录阶段可以maxTimes10、间隔 2 秒提交订单阶段建议间隔更短但次数别太多因为余票窗口很短等太久票就没了。提交订单时要把secret、乘客信息、车次一起 POST 出去返回里判断是否含「订单成功」关键字成功就触发弹窗和声音。注意原文明确说密码是明文存在 Cookie 里的公共电脑上别勾「记住密码」这是设计上的取舍不是 bug。5. 避坑与排查那些跑不通的真实原因5.1 现象双击 start.bat 窗口一闪而过原因java不在 PATH 里或者 JRE 版本低于 1.6java -jar直接失败退出。解决命令行手动执行java -jar GOHOME.jar看完整报错确认java -version有输出start.bat末尾保留pause让窗口停住。5.2 现象登录一直失败日志刷「繁忙」原因验证码识别率低原文说十次对一次或者请求间隔太短被限流。解决关掉自动识别改手动输入把重试间隔从 1 秒调到 23 秒检查 Cookie 是否在登录后被正确回写掉 Cookie 会表现为「登录成功但查询要求重新登录」。5.3 现象查到票了但提交订单总报错原因提交时用的隐藏字段secret、token是旧的或者漏带了某个表单字段。解决每次提交前重新走一遍查询拿最新隐藏串用浏览器开发者工具对照真实提交的 Form Data逐字段核对少一个都不行。5.4 现象指定了车次却抢到别的车原因traincode是「优先」不是「只买」指定车次无票时程序按设计自动选其他车次。解决这是预期行为原文写得很清楚如果只想买某一趟得改代码把过滤逻辑从「排序优先」改成「硬过滤」但代价是无票时一直空转。5.5 现象抢到票但没付上款原因程序只负责下单占位支付要人工登录网页在 45 分钟内完成。解决抢到后立刻去「我的订单」支付别关程序别断网提前把支付方式准备好减少操作时间。6. 二次开发把「能跑」改成「好用」的几个方向拿到这份源码最值得动的不是界面是三个地方。第一是验证码识别原文用颜色像素思路准确率有限可以换成「先二值化再按字符切分」的流程读图、灰度、按阈值转黑白、按列投影找字符间隙、逐字符比对模板。核心是把BufferedImage的像素矩阵玩熟// 简单二值化灰度大于阈值置白否则置黑 public BufferedImage binarize(BufferedImage src, int threshold) { int w src.getWidth(), h src.getHeight(); BufferedImage out new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY); for (int x 0; x w; x) { for (int y 0; y h; y) { int rgb src.getRGB(x, y); int gray (rgb 16 0xff) * 30 (rgb 8 0xff) * 59 (rgb 0xff) * 11; out.setRGB(x, y, gray / 100 threshold ? 0xFFFFFF : 0x000000); } } return out; }阈值threshold一般取 100140 之间试不同批次的验证码底色不一样得拿几十张样本调。第二是重试策略把固定间隔改成「指数退避 随机抖动」避免所有请求卡在同一秒。第三是配置热加载现在改config.properties要重启可以加个WatchService监听文件变更改完自动重读。验证改造是否有效我的习惯是先用「回放」的方式把一次真实查询的 HTML 存成本地文件让解析模块直接读文件跑不依赖网络这样调解析逻辑时不会因为网络抖动误判。等解析稳定了再接真实请求。说个我自己的教训。早些年我改这类工具总想一步到位把识别率拉到很高结果在图像处理上耗了两周票没抢到几张。后来才明白抢票的瓶颈往往不在识别而在「请求节奏」和「隐藏字段的时效性」——识别错一次重试就行字段带错一次直接出局。从那以后我每次动这类自动化流程都强制先把「一次成功提交需要哪些字段、每个字段从哪个页面来」列成表再写代码。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑