12306 自动抢票脚本全解析:从模块拆解到实战应用
在日常出行中,12306 抢票往往是令人头疼的环节。本文将对一款基于 Selenium 实现的 12306 自动抢票脚本进行全方位拆解,从模块功能、代码逻辑到实战注意事项,帮助读者深入理解并灵活运用该脚本。
一、脚本整体架构与核心依赖
该脚本采用面向对象的编程思想,封装为TrainTicketBot类,核心依赖selenium库实现浏览器自动化操作,time库用于控制操作间隔。整体流程可概括为:初始化浏览器配置→等待用户扫码登录→自动填写车次信息→提交购票订单。
核心依赖安装
pip install selenium
# 注意:需匹配Chrome浏览器版本下载ChromeDriver,并配置系统环境变量
二、模块逐行拆解与功能解析
1. 导入模块段
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
time:提供时间休眠功能,避免操作过快触发 12306 反爬机制,同时等待页面元素加载。webdriver:Selenium 核心模块,用于启动浏览器并执行自动化操作。By:指定元素定位方式(如 XPATH、ID、CLASS_NAME 等),是 Selenium 定位页面元素的核心枚举类。Options:Chrome 浏览器配置选项类,用于定制浏览器启动参数(如禁用自动化检测、最大化窗口等)。
2. 类初始化方法(init)
def __init__(self, from_station, to_station, departure_time):
self.from_st = from_station # 出发站
self.to_st = to_station # 到达站
self.de_time = departure_time # 出发日期
self.options = Options()
# 禁用Chrome的自动化检测提示,规避12306反爬机制
self.options.add_experimental_option('excludeSwitches', ['enable-automation'])
self.options.add_argument('--disable-blink-features=AutomationControlled')
self.options.add_argument('--disable-infobars') # 禁用浏览器信息提示栏
self.driver = webdriver.Chrome(self.options) # 启动Chrome浏览器
self.driver.maximize_window() # 最大化浏览器窗口,避免元素定位偏差
核心功能:
- 接收外部传入的出发站、到达站、出发日期参数并赋值为实例属性;
- 配置 Chrome 浏览器启动参数,核心是规避 12306 对自动化工具的检测;
- 初始化浏览器实例并最大化窗口,确保页面元素完整展示。
3. 元素存在性校验方法(is_element_exist)
def is_element_exist(self, element):
# 判断元素是否存在
flag = True
try:
self.driver.find_element(By.XPATH, element)
return flag
except:
flag = False
return flag
核心功能:
- 封装通用的元素存在性校验逻辑,通过 XPATH 定位元素;
- 利用 try-except 捕获元素未找到的异常,返回布尔值标识元素是否存在;
- 解决页面加载异步性问题,避免因元素未加载完成导致脚本报错。
4. 核心抢票逻辑方法(check_ticket)
该方法是脚本的核心,可拆分为 6 个关键子流程:
子流程 1:打开登录页面并等待扫码登录
# 打开登录页面
self.driver.get("https://kyfw.12306.cn/otn/resources/login.html")
# 判断是否登录成功
while not self.is_element_exist('//a[@class="txt-primary underline"]'):
# 循环等待用于扫码登录
time.sleep(1)
driver.get():加载 12306 登录页面;- 循环调用
is_element_exist校验登录成功标识元素(登录后出现的 “我的 12306” 类链接),每 1 秒检查一次,直到用户完成扫码登录,确保后续操作基于已登录状态。
子流程 2:跳转至车票查询页面
self.driver.find_element(By.XPATH, '//a[@class="txt-primary underline"]').click()
time.sleep(1)
- 定位并点击登录成功后的跳转链接,进入车票预订页面;
- 休眠 1 秒等待页面跳转完成,避免操作提前导致元素定位失败。
子流程 3:自动填写出发 / 到达站信息
# 定位出发地输入框,并输入站点名
self.driver.find_element(By.XPATH, '//input[@id="fromStationText"]').click()
self.driver.find_element(By.XPATH, '//input[@id="fromStationText"]').send_keys(self.from_st)
self.driver.find_element(By.XPATH, '//span[@class="ralign"]').click()
time.sleep(0.5)
# 定位目的地输入框,并输入站点名
self.driver.find_element(By.XPATH, '//input[@id="toStationText"]').click()
self.driver.find_element(By.XPATH, '//input[@id="toStationText"]').send_keys(self.to_st)
self.driver.find_element(By.XPATH, '//span[@class="ralign"]').click()
time.sleep(0.5)
- 操作逻辑:点击输入框→输入站点名称→点击确认按钮;
- 每个操作后休眠 0.5 秒,模拟人工操作节奏,避免触发反爬;
- 注意:XPATH 定位需与 12306 页面 DOM 结构匹配,若页面更新需同步调整。
子流程 4:填写出发日期并查询车票
# 定位日期输入框,并输入对应的日期
self.driver.find_element(By.XPATH, '//input[@id="train_date"]').clear()
self.driver.find_element(By.XPATH, '//input[@id="train_date"]').send_keys(self.de_time)
time.sleep(0.5)
# 定位搜索按钮,并点击搜索
self.driver.find_element(By.XPATH, '//a[@id="query_ticket"]').click()
time.sleep(0.5)
clear():清空日期输入框默认值,避免残留内容影响输入;send_keys():输入指定格式的出发日期(如 2025-09-01);- 点击 “查询车票” 按钮,加载符合条件的车次列表。
子流程 5:预订车票并选择乘车人
# 点击预定车票
self.driver.find_element(By.XPATH, '//a[text()="预订"]').click()
time.sleep(0.5)
# 选择乘车人
self.driver.find_element(By.XPATH, '//input[@title="设置为乘车人,按空格键进行操作"]').click()
time.sleep(0.5)
print('选择成功')
# 点击提交订单
self.driver.find_element(By.XPATH, '//a[text()="提交订单"]').click()
print('提交成功')
time.sleep(3)
# 确认订单(二维码提交)
self.driver.find_element(By.ID, 'qr_submit_id').click()
print('确认成功')
- 定位 “预订” 按钮并点击,进入乘车人选择页面;
- 选择预设的乘车人(需提前在 12306 账号中添加);
- 提交订单并点击确认按钮,最终生成支付订单(需用户 15 分钟内完成支付)。
子流程 6:异常处理与提示
except Exception as e:
print('================选票重试中================')
print('请在15分钟内支付订单')
time.sleep(60)
- 捕获操作过程中的所有异常(如元素定位失败、页面加载超时等),打印重试提示;
- 提示用户在 15 分钟内完成支付,休眠 60 秒保留页面供用户操作。
5. 主程序入口
if __name__ == '__main__':
# 定义出发地
from_station = '长沙'
# 定义目的地
to_station = '北京西'
# 设置出行日期(最多只能设置15天内)
departure_time = '2025-09-01'
# 实例化类
T = TrainTicketBot(from_station, to_station, departure_time)
# 开始自动抢票
T.check_ticket()
- 定义脚本运行的核心参数(出发站、到达站、出发日期);
- 实例化
TrainTicketBot类并调用check_ticket方法启动抢票流程。
三、脚本优化与实战注意事项
1. 现有脚本的局限性
- 未实现车次筛选:默认点击第一个 “预订” 按钮,无法指定车次、席别;
- 无重试机制:仅捕获异常打印提示,未实现自动重新查询;
- 定位方式脆弱:XPATH 依赖页面 DOM 结构,12306 页面更新后脚本会失效;
- 未处理验证码:若登录过程中出现验证码,脚本会卡住。
2. 优化方向
(1)增加车次筛选功能
# 在check_ticket方法中,查询车票后添加车次筛选
def filter_train(self, train_num):
# 定位所有车次行
train_rows = self.driver.find_elements(By.XPATH, '//tbody[@id="queryLeftTable"]/tr')
for row in train_rows:
# 获取车次编号
train_id = row.find_element(By.XPATH, './td[1]/div/div[1]/div/a').text
if train_id == train_num:
# 点击该车次的预订按钮
row.find_element(By.XPATH, './td[last()]/a').click()
return True
return False
(2)实现循环抢票逻辑
def check_ticket(self):
self.driver.get("https://kyfw.12306.cn/otn/resources/login.html")
while not self.is_element_exist('//a[@class="txt-primary underline"]'):
time.sleep(1)
self.driver.find_element(By.XPATH, '//a[@class="txt-primary underline"]').click()
time.sleep(1)
# 循环抢票,直到成功
while True:
try:
# 原有填写信息逻辑...
# 新增车次筛选
self.filter_train('G508') # 指定车次
# 原有选择乘车人、提交订单逻辑...
break # 成功后退出循环
except Exception as e:
print(f'抢票失败,重试中:{e}')
time.sleep(2) # 失败后休眠2秒重新查询
3. 实战安全注意事项
- 遵守 12306 用户协议:自动化脚本可能违反平台规定,过度使用可能导致账号封禁;
- 控制操作频率:避免短时间内高频次请求,建议将休眠时间设置为 1-3 秒;
- 及时更新定位规则:12306 页面会不定期更新,需同步调整 XPATH/ID 等定位符;
- 保护账号安全:脚本中避免硬编码账号密码,建议通过扫码登录方式操作。
四、总结
本文对 12306 自动抢票脚本的核心模块、代码逻辑进行了全面拆解,分析了脚本的设计思路与局限性,并给出了优化方向。该脚本本质是利用 Selenium 模拟人工操作,核心价值在于简化重复的购票流程,但在实际使用中需兼顾平台规则与反爬机制。

168万+

被折叠的 条评论
为什么被折叠?



