公司动态
Python 实现 12306 自动查票、抢票
前言每到节假日、春运火车票就会出现一票难求的情况。很多技术爱好者会好奇如何利用 Python 实现 **12306 自动查票、抢票**功能。本文基于 **Selenium 浏览器自动化**技术讲解整套抢票脚本的**设计思路、核心流程、关键技术点与核心代码片段**仅做**编程技术学习、浏览器自动化练习**使用。⚠️重要声明本文所有内容仅用于技术交流与学习禁止将脚本用于恶意刷票、囤票、扰乱铁路购票秩序。使用第三方脚本抢票违反 12306 用户协议可能造成账号封禁、IP 限制请大家文明购票、遵守平台规则与相关法律法规。一、项目整体介绍1. 实现功能本项目采用 Selenium 驱动 Chrome 浏览器模拟人工操作 12306 网页主要实现能力1. 自动绕过 WebDriver 检测规避基础反爬2. 自动打开网页、唤起扫码登录规避复杂验证码3. 自动填充出发地、目的地、出行日期4. 高频快速轮询查询余票精准识别可预订车次5. 多车次优先级抢购首选车次无票自动切换备选车次6. 区分高铁 / 普速列车自动选择对应座位类型7. 自动选择乘车人、提交订单下单后提醒用户手动支付。2. 技术栈- Selenium浏览器自动化核心库模拟鼠标、键盘、页面点击- ChromeDriverChrome 浏览器驱动配合 Selenium 使用- Python 基础库time、random实现延时、防风控- 前端 JS 注入利用 JavaScript 操作 DOM大幅提升抢票速度。为了方便维护和修改参数项目采用**配置文件 主逻辑文件**分离的结构不把所有参数硬编码在业务代码中。二、主程序核心思路与关键代码解析核心优化高速余票检测函数传统 Selenium 循环遍历元素速度很慢也是很多抢票脚本卡顿的主要原因。本项目采用 **JS 注入方式**直接操作页面 DOM跳过元素定位耗时毫秒级识别可预订车次。关键代码片段1python def check_available_tickets(driver): # 执行JS代码快速筛选出带有预订按钮、非候补的有效车次 tickets_info driver.execute_script( const tickets []; const rows document.querySelectorAll(#queryLeftTable tr:not([style*display: none])); for (const row of rows) { const trainNumber row.querySelector(.number)?.textContent; const bookingBtn row.querySelector(.btn72); const ywStatus row.querySelector(td:nth-child(8))?.textContent; if (bookingBtn trainNumber ywStatus) { tickets.push({trainNumber, hasButton: true, status: ywStatus}); } } return tickets; ) return tickets_info 技术亮点1. 直接通过选择器筛选可见车次行2. 同时判断**是否有预订按钮**、**是否为候补票**过滤无效车次3. JS 执行效率远高于 Selenium 逐层元素查找是提升抢票速度的核心优化。绕过 12306 自动化检测12306 会检测浏览器 webdriver 标识以此判断当前是否为自动化脚本。我们通过 Chrome 调试协议CDP在页面加载前篡改该属性实现基础绕过。关键代码片段2python # 注入代码隐藏 webdriver 特征绕过网站自动化检测 driver.execute_cdp_cmd( Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, {get: () undefined}) } ) 该代码放在浏览器打开网页之前执行能有效规避账号密码登录时的滑动验证码拦截。 核心思路优先使用 JS 点击配合合理等待避免元素遮挡、加载失败问题。自动填充出发地、目的地、日期模拟人工在输入框填写站点与日期结合 Keys.ENTER 确认下拉联想选项完全贴合人工操作逻辑python # 示例自动填充出发地 driver.find_element(By.XPATH, //*[idfromStationText]).click() driver.find_element(By.XPATH, //*[idfromStationText]).clear() driver.find_element(By.XPATH, //*[idfromStationText]).send_keys(conf.from_station) time.sleep(1) driver.find_element(By.XPATH, //*[idfromStationText]).send_keys(Keys.ENTER) 目的地、日期填充逻辑与上方一致格式统一、便于修改。核心轮询持续查票 多车次抢购逻辑这是整个脚本**最核心的业务逻辑**采用死循环不断查询余票一旦检测到可预订车票立即按配置的车次优先级执行抢购。核心逻辑梳理1. 循环判断查询按钮状态按钮可点击则自动刷新车次列表2. 调用高速余票检测函数判断当前是否有可预订车票3. 无票则随机短延时继续下一轮查询随机延时降低风控概率4. 有票则遍历**优先级车次列表**逐个匹配目标车次5. 匹配成功后JS 点击预订按钮关闭弹窗、选择乘车人6. 区分**高铁 / 普速列车**自动选择对应座位类型7. 提交订单引导用户 10 分钟内手动完成支付。关键代码片段轮询主体python # 定位查询按钮 query_tag driver.find_element(By.XPATH, //*[idquery_ticket]) while True: # 按钮可用则刷新车票 if query_tag.get_attribute(class) btn92s: driver.execute_script(arguments[0].click();, query_tag) # 获取当前可预订车次 available_tickets check_available_tickets(driver) if not available_tickets: print(暂无可预订车票继续监控...) time.sleep(random.uniform(0.097, 0.345)) continue # 按优先级尝试抢购多个车次 for train_number in conf.train_numbers: # 匹配目标车次排除候补票 target_ticket next( ( t for t in available_tickets if t[trainNumber] train_number and 候补 not in t[status] ), None ) if target_ticket: # 执行预订、选乘车人、选座位、提交订单逻辑 print(f成功锁定车次{train_number}请尽快支付) return 分车型座位选择适配针对高铁G/D、普速列车Z/K两种车型做差异化座位选择1. **高铁 / 动车**默认选择靠窗 F 座通过 JS 直接点击选座元素2. **普速列车**通过下拉框选择硬座 / 硬卧 / 软卧通过 JS 修改下拉框选中状态并触发事件。该部分大量使用 JS 操作页面元素解决普通点击不稳定、下拉框选择失效等问题。三、风险提示与使用规范必看1. **合规第一**本代码仅用于 **Selenium 自动化、前端 JS 交互、爬虫技术学习**严禁用于商业、恶意抢票、批量囤票2. **账号安全**建议使用小号测试高频刷新、频繁提交订单有账号封禁风险3. **IP 限制**不要将延时设置过小高频请求会导致 IP 被临时限制访问 123064. **功能边界**本脚本仅实现**自动下单**支付环节保留人工操作不涉及任何破解、绕过支付流程的逻辑5. **页面变动**12306 会不定期更新页面结构、元素 ID、XPath代码可能随页面改版失效需自行微调元素定位规则。四、拓展学习方向纯技术提升如果想继续深入学习浏览器自动化、爬虫相关技术可以基于本项目拓展以下方向1. 增加**邮件 / 微信消息推送**抢到票后主动提醒用户2. 学习异步请求 aiohttp结合接口实现纯接口版余票监控3. 研究图片验证码、滑块验证码识别技术纯图像识别学习4. 增加日志功能记录每一轮查询状态与时间5. 学习代理 IP 使用理解风控与反风控的技术原理。五、写在最后12306 抢票脚本本质就是**浏览器自动化模拟人工操作**核心难点在于页面元素适配、反检测、执行速度优化。希望本文能帮助大家理解 Selenium 实战、JS 注入、自动化脚本的设计思路。再次呼吁技术要用到正道上出行购票优先使用官方 App、小程序共同维护公平的购票环境。如果文章对你有帮助欢迎点赞、收藏、评论交流技术问题