1. 项目背景与需求解析
每到节假日,抢火车票就成了许多人的噩梦。早上8点准时开抢,网页卡死、验证码识别失败、提交订单时票已售罄...这些场景相信大家都经历过。作为一名经常需要跨城出行的程序员,我决定用技术手段解决这个痛点。
这个自动化购票工具的核心价值在于:通过程序模拟人工操作流程,实现7×24小时监控余票、自动提交订单、智能识别验证码等功能。相比人工抢票,它能以毫秒级速度完成操作,在票源释放的第一时间完成锁定。
重要提示:本工具仅作为技术研究用途,需遵守相关平台使用规则。建议仅在个人合理购票需求范围内使用,避免对公共服务系统造成压力。
2. 技术方案选型与架构设计
2.1 主流技术路线对比
目前实现自动化购票主要有三种技术路径:
浏览器自动化方案(如Selenium/Puppeteer)
- 优点:完全模拟人工操作,行为最接近真实用户
- 缺点:执行效率较低,依赖浏览器环境
接口直接调用方案
- 优点:响应速度最快,性能最优
- 缺点:需要逆向分析接口,维护成本高
混合方案
- 结合前两者优势,关键环节走接口,必要步骤用浏览器模拟
经过实测,我最终选择了混合方案。核心考虑因素包括:
- 购票流程中验证码环节必须模拟人工操作
- 余票查询和提交订单需要毫秒级响应
- 需要保持长期稳定运行不封号
2.2 系统架构设计
工具的核心模块组成如下:
[定时任务调度] → [余票监控模块] → [订单预处理模块] ↓ ↓ [消息通知中心] ← [验证码识别引擎] ← [订单提交模块]关键组件说明:
- 余票监控:采用多线程轮询,间隔时间动态调整(高峰期1秒/次)
- 验证码识别:CNN模型+传统图像处理结合,准确率可达92%
- 异常处理:自动识别系统繁忙、登录失效等状态并恢复
3. 环境准备与工具配置
3.1 基础环境搭建
推荐使用Python 3.8+环境,主要依赖库:
pip install selenium requests pillow opencv-python numpy浏览器驱动配置(以Chrome为例):
- 下载对应版本的chromedriver
- 配置系统PATH环境变量
- 测试驱动是否正常工作:
from selenium import webdriver driver = webdriver.Chrome() driver.get("https://www.12306.cn")3.2 关键参数配置
创建config.ini配置文件,包含以下核心参数:
[account] username = 您的12306账号 password = 加密后的密码 [query] from_station = BJ # 北京 to_station = SHH # 上海 date = 2023-10-01 train_codes = G1,G3,G5 # 优先车次 [settings] retry_times = 5 # 失败重试次数 notify_email = your@email.com安全提示:密码建议使用AES加密存储,不要在配置文件中明文保存
4. 核心功能实现详解
4.1 余票监控模块
实现代码框架:
def ticket_monitor(): while True: try: # 构建查询URL params = { 'leftTicketDTO.train_date': config.date, 'leftTicketDTO.from_station': station_map[config.from_station], 'leftTicketDTO.to_station': station_map[config.to_station], 'purpose_codes': 'ADULT' } # 发送查询请求 response = requests.get( 'https://kyfw.12306.cn/otn/leftTicket/query', params=params, headers=generate_headers() ) # 解析余票信息 trains = parse_trains(response.json()) # 检查目标车次 for train in trains: if train['train_no'] in config.train_codes: if check_seat_available(train): trigger_order(train) break # 动态调整查询间隔 time.sleep(get_interval()) except Exception as e: log_error(e) time.sleep(60)关键优化点:
- 使用连接池复用HTTP连接
- 响应数据采用增量解析
- 异常情况自动降级查询频率
4.2 验证码识别方案
验证码处理流程:
图像预处理
- 灰度化 + 二值化
- 降噪处理(中值滤波)
- 字符分割(投影法)
识别模型
- 使用预训练的CNN模型
- 数据增强提升泛化能力
- 集成多个模型投票决策
代码示例:
def recognize_captcha(image): # 预处理 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV) # 字符分割 vertical_proj = np.sum(binary, axis=0) char_positions = find_peaks(vertical_proj) # 加载模型 model = load_model('captcha_model.h5') # 逐个识别字符 result = [] for i in range(len(char_positions)-1): char_img = binary[:, char_positions[i]:char_positions[i+1]] char_img = preprocess_char(char_img) pred = model.predict(char_img) result.append(CLASSES[np.argmax(pred)]) return ''.join(result)实测准确率提升技巧:
- 添加本地样本微调模型
- 对模糊样本进行超分辨率重建
- 结合传统模板匹配作为后备方案
5. 订单提交与异常处理
5.1 订单提交全流程
初始化会话
- 模拟登录获取cookies
- 保持心跳维持会话
预提交检查
- 检查用户身份信息有效性
- 验证乘车人资格
正式提交
- 构造订单提交参数
- 处理排队等待情况
- 获取订单确认页面
def submit_order(train_info): # 检查登录状态 check_login() # 获取token token = get_submit_token() # 构造提交数据 data = { 'secretStr': train_info['secretStr'], 'train_date': config.date, 'back_train_date': config.date, 'tour_flag': 'dc', 'purpose_codes': 'ADULT', 'query_from_station_name': config.from_station, 'query_to_station_name': config.to_station, 'undefined': '' } # 提交订单 response = requests.post( 'https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest', data=data, headers={'Referer': 'https://kyfw.12306.cn/otn/leftTicket/init'} ) # 处理响应 if response.json()['status']: return process_order_confirm() else: raise SubmitError(response.json()['messages'])5.2 常见异常处理方案
| 异常类型 | 触发场景 | 解决方案 |
|---|---|---|
| 登录失效 | Cookies过期 | 自动重新登录 |
| 验证码错误 | 识别失败3次 | 切换识别模型 |
| 系统繁忙 | 高峰期访问 | 指数退避重试 |
| 余票变化 | 提交时无票 | 回滚到监控状态 |
| 身份校验 | 乘车人无效 | 切换备用乘车人 |
重试策略建议:
- 首次失败:立即重试
- 第二次失败:等待1秒
- 后续失败:等待时间指数增长
- 超过5次:触发报警通知
6. 高级功能与优化技巧
6.1 智能候补策略
当直达车次无票时,自动启用智能候补方案:
多段联程计算
- 北京→郑州→上海
- 自动计算中转停留时间
跨车次抢票
- 同一线路不同车次组合
- 智能匹配最佳衔接方案
席位降级策略
- 优先二等座→无票转一等座
- 卧铺→座位→站票分级处理
6.2 性能优化实战
通过以下优化将平均响应时间从1.2s降至300ms:
请求优化
- 合并余票查询请求
- 使用HTTP/2多路复用
- 启用gzip压缩
本地缓存
- 车站编码缓存
- 车次信息缓存
- 验证码识别结果缓存
并发控制
- 令牌桶限流算法
- 自适应并发调整
- 关键操作加锁
class TokenBucket: def __init__(self, capacity, fill_rate): self.capacity = capacity self._tokens = capacity self.fill_rate = fill_rate self.timestamp = time.time() def consume(self, tokens=1): now = time.time() delta = self.fill_rate * (now - self.timestamp) self._tokens = min(self.capacity, self._tokens + delta) self.timestamp = now if self._tokens >= tokens: self._tokens -= tokens return True return False7. 部署方案与使用指南
7.1 本地运行方案
推荐使用虚拟环境隔离:
python -m venv venv source venv/bin/activate # Linux/Mac venv\Scripts\activate # Windows pip install -r requirements.txt python main.py --config your_config.ini7.2 服务器部署建议
基础配置
- 2核4G云服务器
- 定时任务监控进程
- 日志轮转配置
高可用方案
- 多节点热备
- 心跳检测自动切换
- 异地容灾部署
监控报警
- Prometheus监控关键指标
- 企业微信/邮件报警
- 自动恢复机制
7.3 使用技巧分享
购票时机选择
- 放票后45分钟(未支付回票)
- 发车前15天(退票高峰)
- 发车前1天(最终退票)
车次选择策略
- 优先选择非热门车次
- 关注新增临客车次
- 跨局车次余票更多
账户安全建议
- 单独注册购票账号
- 开启登录保护
- 定期修改密码
8. 法律合规与道德使用
8.1 合规使用要点
频率控制
- 查询间隔≥1秒
- 避免高峰时段密集访问
- 设置合理的重试次数
账户安全
- 不使用他人账号
- 不公开账号信息
- 不尝试破解系统
使用范围
- 仅限个人合理需求
- 不用于商业代抢
- 不干扰正常用户
8.2 技术伦理思考
作为开发者需要权衡:
- 技术便利性与系统公平性
- 个人需求与公共利益
- 工具价值与社会责任
建议遵循原则:
- 不以盈利为目的
- 不影响他人正常购票
- 不破坏系统稳定性
在实际使用中,我通常会设置以下限制:
- 仅监控1-2个目标车次
- 每天总查询量<1000次
- 成功购票后自动停止
这些经验分享希望能帮助大家在合理合法的前提下,更高效地解决购票难题。技术应该让生活更美好,而不是制造新的不公平。如果大家在实现过程中遇到任何技术问题,也欢迎交流讨论。