## 前言
每到节假日、春运,火车票就会出现一票难求的情况。很多技术爱好者会好奇,如何利用 Python 实现 **12306 自动查票、抢票**功能。
本文基于 **Selenium 浏览器自动化**技术,讲解整套抢票脚本的**设计思路、核心流程、关键技术点与核心代码片段**,仅做**编程技术学习、浏览器自动化练习**使用。
> ⚠️ **重要声明:** 本文所有内容仅用于技术交流与学习,禁止将脚本用于恶意刷票、囤票、扰乱铁路购票秩序。使用第三方脚本抢票违反 12306 用户协议,可能造成账号封禁、IP 限制,请大家文明购票、遵守平台规则与相关法律法规。
## 一、项目整体介绍
### 1. 实现功能
本项目采用 Selenium 驱动 Chrome 浏览器模拟人工操作 12306 网页,主要实现能力:
1. 自动绕过 WebDriver 检测,规避基础反爬;
2. 自动打开网页、唤起扫码登录(规避复杂验证码);
3. 自动填充出发地、目的地、出行日期;
4. 高频快速轮询查询余票,精准识别可预订车次;
5. 多车次优先级抢购:首选车次无票自动切换备选车次;
6. 区分高铁 / 普速列车,自动选择对应座位类型;
7. 自动选择乘车人、提交订单,下单后提醒用户手动支付。
### 2. 技术栈
- `Selenium`:浏览器自动化核心库,模拟鼠标、键盘、页面点击;
- `ChromeDriver`:Chrome 浏览器驱动,配合 Selenium 使用;
- Python 基础库:`time`、`random`,实现延时、防风控;
- 前端 JS 注入:利用 JavaScript 操作 DOM,大幅提升抢票速度。
为了方便维护和修改参数,项目采用**配置文件 + 主逻辑文件**分离的结构,不把所有参数硬编码在业务代码中。
## 二、主程序核心思路与关键代码解析
### 核心优化:高速余票检测函数
传统 Selenium 循环遍历元素速度很慢,也是很多抢票脚本卡顿的主要原因。本项目采用 **JS 注入方式**直接操作页面 DOM,跳过元素定位耗时,毫秒级识别可预订车次。
#### 关键代码片段
```python
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)在页面加载前篡改该属性,实现基础绕过。
#### 关键代码片段
```python
# 注入代码,隐藏 webdriver 特征,绕过网站自动化检测
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": """
Object.defineProperty(navigator, 'webdriver', {get: () => undefined})
"""
}
)
```
该代码放在浏览器打开网页之前执行,能有效规避账号密码登录时的滑动验证码拦截。
> 核心思路:优先使用 JS 点击,配合合理等待,避免元素遮挡、加载失败问题。
### 自动填充出发地、目的地、日期
模拟人工在输入框填写站点与日期,结合 `Keys.ENTER` 确认下拉联想选项,完全贴合人工操作逻辑:
```python
# 示例:自动填充出发地
driver.find_element(By.XPATH, '//*[@id="fromStationText"]').click()
driver.find_element(By.XPATH, '//*[@id="fromStationText"]').clear()
driver.find_element(By.XPATH, '//*[@id="fromStationText"]').send_keys(conf.from_station)
time.sleep(1)
driver.find_element(By.XPATH, '//*[@id="fromStationText"]').send_keys(Keys.ENTER)
```
目的地、日期填充逻辑与上方一致,格式统一、便于修改。
### 核心轮询:持续查票 + 多车次抢购逻辑
这是整个脚本**最核心的业务逻辑**,采用死循环不断查询余票,一旦检测到可预订车票,立即按配置的车次优先级执行抢购。
#### 核心逻辑梳理
1. 循环判断查询按钮状态,按钮可点击则自动刷新车次列表;
2. 调用高速余票检测函数,判断当前是否有可预订车票;
3. 无票则随机短延时,继续下一轮查询(随机延时降低风控概率);
4. 有票则遍历**优先级车次列表**,逐个匹配目标车次;
5. 匹配成功后,JS 点击预订按钮,关闭弹窗、选择乘车人;
6. 区分**高铁 / 普速列车**,自动选择对应座位类型;
7. 提交订单,引导用户 10 分钟内手动完成支付。
#### 关键代码片段(轮询主体)
```python
# 定位查询按钮
query_tag = driver.find_element(By.XPATH, '//*[@id="query_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 被临时限制访问 12306;
4. **功能边界**:本脚本仅实现**自动下单**,支付环节保留人工操作,不涉及任何破解、绕过支付流程的逻辑;
5. **页面变动**:12306 会不定期更新页面结构、元素 ID、XPath,代码可能随页面改版失效,需自行微调元素定位规则。
## 四技、拓展学习方向(纯术提升)
如果想继续深入学习浏览器自动化、爬虫相关技术,可以基于本项目拓展以下方向:
1. 增加**邮件 / 微信消息推送**,抢到票后主动提醒用户;
2. 学习异步请求 `aiohttp`,结合接口实现纯接口版余票监控;
3. 研究图片验证码、滑块验证码识别技术(纯图像识别学习);
4. 增加日志功能,记录每一轮查询状态与时间;
5. 学习代理 IP 使用,理解风控与反风控的技术原理。
## 五、写在最后
12306 抢票脚本本质就是**浏览器自动化模拟人工操作**,核心难点在于页面元素适配、反检测、执行速度优化。希望本文能帮助大家理解 Selenium 实战、JS 注入、自动化脚本的设计思路。
再次呼吁:技术要用到正道上,出行购票优先使用官方 App、小程序,共同维护公平的购票环境。如果文章对你有帮助,欢迎点赞、收藏、评论交流技术问题~
本期内容就分享到这里啦!如果你在实践过程中遇到任何问题,或者想获取文中提到的完整源码、
配置文件模板,以及更多进阶玩法,可扫码领取(无套路,纯分享)。

2万+

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



