ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Selenium的12306自动抢票脚本:原理、实现与避坑指南

基于Selenium的12306自动抢票脚本:原理、实现与避坑指南 简介基于Selenium的12306自动抢票脚本全套资料面向计算机相关专业学生、教师及企业开发者可解决购票脚本设计、自动化测试与实战演练等需求。压缩包共18个文件涵盖7个Python源码、5个XML工程配置、3个文本说明及YAML环境配置等其中py文件实现核心抢票逻辑、车站信息获取、参数配置与邮件提醒等模块yaml与xml用于配置和工程管理md与txt提供详细使用文档。资源已通过运行测试并获导师认可、答辩评审95分适合用于毕业设计、课程设计、项目初期演示或Selenium进阶学习。目前已有129人学习下载配套详细文档能帮助读者快速理解自动化购票流程包括余票查询、提交订单等关键环节目录结构清晰便于按模块阅读和二次开发。1. 基于Selenium的12306自动抢票脚本它不是外挂是替你盯着余票的浏览器每年春运一到朋友圈里就会冒出一堆“代抢票”的链接还总有人问“为什么别人手速那么快明明显示有票一点进去就没了”其实问题不在于手速而在于你盯着页面、刷新、对比车次、再点预定、再选乘客的速度。基于Selenium的12306自动抢票脚本做的事情就是把这套“人肉操作”变成浏览器自动执行定时刷新余票、按你预设的车次和坐席过滤、出现目标车票就点预定、填好乘客、提交订单。它不是破解12306的接口也没有绕过任何风控逻辑只是在用程序控制一个真实浏览器模拟一个始终在线且不提手酸的“真人”。所以它最大的价值不是“抢”而是让脚本帮你把“盯”这件事做到一天不眨眼。适合通勤票、假期票、帮家里老人抢票也适合想拿自动化框架练手的开发者前提是你愿意把登录、验证码这些环节留出人工兜底。2. 为什么选Selenium做抢票选型理由与最少可运行环境在动手之前先把选型这事说透。很多人看到“自动抢票脚本”第一反应是抓12306的后台接口直接POST提交订单。这条路对开发者的诱惑很大但12306的核心下单接口做过签名校验关键参数由前端加密生成抓包抓到的明文请求根本没法直接复用。更现实的问题是接口方案一旦失效你面对的不是改个参数的事而是要把整套签名算法逆向出来维护成本极高。Selenium走的是另一条路它不碰接口而是驱动浏览器按真实用户的路径操作页面。12306的风控再严也不可能把正常打开网页的人全拦住对吧这就是Selenium做自动化抢票的核心优势——黑匣子最小化。2.1 Selenium凭什么能抢票浏览器自动化与真人操作的边界Selenium是一个WebDriver自动化测试框架常见用法是让测试脚本去驱动Chrome、Firefox这类浏览器模拟点击、输入、滚动、等待这些行为。它最早是给测试人员做回归测试用的后来被广泛用于自动化运营、数据采集自然也就有人拿它来做抢票脚本。它的工作模式是“客户端库 浏览器驱动”你在Python里调用selenium包它通过ChromeDriver这类驱动件把指令翻译成浏览器能理解的动作再由浏览器真实地渲染页面、执行JavaScript。这种模式的边界在哪儿第一Selenium不是万能的它要求目标页面在DOM层面稳定如果12306改版把按钮的class和id全换掉脚本就要跟着改选择器。第二它天然会留下自动化特征比如webdriver属性、奇怪的浏览器指纹风控系统可以识别它成年人要有点自觉任何针对Web的自动化工具都存在被平台识别和管控的可能这不是脚本本身能完全藏住的。第三它处理不了“页面没变但数据已变”的情况余票查询这种场景还是得靠轮询。所以真实项目里Selenium做的是“最后一百米”——把人到按钮的距离缩短为程序到按钮的距离而不是发明一把万能钥匙。2.2 搭环境Python虚拟环境Selenium浏览器驱动的最小步骤环境搭建不复杂但顺序别搞错。先建一个干净的Python虚拟环境然后在里面安装selenium包最后下载与浏览器版本匹配的驱动。很多人一上来直接全局装selenium结果驱动版本和浏览器对不上报一堆SessionNotCreatedException这种翻车完全可以避免。按下面这套来操作# 1. 创建并激活虚拟环境Python 3.9 均可 python -m venv venv_12306 source venv_12306/bin/activate # Windows 下执行 venv_12306\Scripts\activate # 2. 安装 selenium pip install selenium # 3. 检查浏览器版本 google-chrome --version # Windows 下在 Chrome 地址栏输入 chrome://version参数说明虚拟环境解决的是包冲突问题你机器上可能已经装了旧版selenium或者requests抢票脚本对依赖版本比较敏感隔离是必须的。安装selenium不需要指定版本但建议装到4.x以上4.x的API比3.x干净很多不再需要手动声明executable_path。检查浏览器版本这一步别省驱动要和浏览器大版本一致比如Chrome 120就配ChromeDriver 120.x小版本可以略有偏差大版本不能错。接下来是驱动下载。Selenium 4.x提供了Selenium Manager会自动去官方源拉驱动但国内网络条件下经常超时建议还是手动下载。下载后把驱动文件放到一个固定目录比如tools/chromedriver然后在代码里指定路径。2.3 最小启动脚本先跑通一个能打开12306的浏览器环境装好后不要急着写业务逻辑先跑一个最小启动脚本验证整个链路是通的。这一步能过滤掉九成环境问题。代码只做一件事打开一个带用户数据目录的Chrome窗口访问12306的首页然后停在那里等你按回车。为什么要带用户数据目录因为抢票场景需要保持登录状态你手动登录一次后Cookie存在这个目录里下次脚本启动还能继续用不然每次都要重新扫码登录。from selenium import webdriver options webdriver.ChromeOptions() # 使用自定义用户数据目录保存登录态 options.add_argument(--user-data-dir/path/to/chrome_user_data) # 关闭正在受到自动软件控制提示条 options.add_experimental_option(excludeSwitches, [enable-automation]) # 可选去掉 webdriver 特征警惕这属于反检测实践需自行评估风险 options.add_argument(--disable-blink-featuresAutomationControlled) driver webdriver.Chrome(optionsoptions) driver.get(https://www.12306.cn/index/) print(浏览器已打开请手动确认是否进入官网) input(按回车键退出...) driver.quit()逻辑说明代码先给Chrome配置一组选项再用这些选项生成driver实例打开12306首页最后用input()把进程挂住好让你肉眼看一眼浏览器是不是真的正常加载了页面。--user-data-dir这个参数是整套脚本的地基登录态能不能跨脚本保持就看它配没配对。excludeSwitches和--disable-blink-features是控制自动化提示的常用做法但你要清楚这类参数只是降低存在感不能保证绝对不被平台识别——后面避坑章节我会再细说。参数说明/path/to/chrome_user_data要替换成你的绝对路径注意Windows路径里的反斜杠要写成双反斜杠或使用r...原始字符串。--user-data-dir指向的是一个空目录也没关系Chrome会自动初始化。这里有个常见坑如果你之前已经开着一个Chrome进程再用同一个用户目录启动脚本会直接报“user data directory is already in use”脚本启动前先关掉其他Chrome窗口。跑通这一步你的Selenium环境就算立住了。下一个问题是有了环境脚本到底怎么写3. 抢票脚本的三个核心模块登录态、余票查询与提交订单这一章是整套脚本的主体我的做法是把流程拆成三个模块登录态管理、余票查询、提交订单。每个模块单独写、单独测最后串起来。千万别一上来就写一个1000行的大脚本出错的时候你连在哪一行挂的都不知道。模块化还有一个好处查询模块和提交模块可以分开调优比如查询频率降下来不影响提交速度提交环节卡了验证码也不影响下次轮询。3.1 登录态管理Cookie与等待策略12306的登录方式有两种账号密码登录和扫码登录。手机App扫码是现在的主流你让脚本去自动识别二维码是不可能的但根本不需要识别。扫码登录的一个特点是登录成功后Cookie会种在浏览器里而且有效期覆盖整个会话。所以我用的策略是“半自动登录”脚本打开登录页你掏出手机扫码扫完以后脚本自动检测跳转然后把Cookie序列化存到本地文件。下次启动直接从文件加载Cookie免登录。代码如下import time import json from selenium import webdriver from selenium.webdriver.common.by import By USER_DATA_DIR /path/to/chrome_user_data COOKIE_FILE 12306_cookies.json options webdriver.ChromeOptions() options.add_argument(f--user-data-dir{USER_DATA_DIR}) driver webdriver.Chrome(optionsoptions) driver.get(https://kyfw.12306.cn/otn/resources/login.html) # 轮询检测是否已跳转到个人中心最多等 120 秒 for _ in range(120): if login not in driver.current_url: break time.sleep(1) # 登录成功后保存 Cookie cookies driver.get_cookies() with open(COOKIE_FILE, w, encodingutf-8) as f: json.dump(cookies, f, ensure_asciiFalse, indent2) print(f已保存 {len(cookies)} 条 Cookie) driver.quit()逻辑说明代码打开12306登录二维码页面然后用一个120次的循环每秒检查一次当前URL——只要URL里不再包含login字样说明登录成功并且页面发生了跳转。跳转后马上执行get_cookies()把Cookie抓下来写进JSON文件。注意这里的time.sleep(1)是轮询等待不是精确计时它的作用是给浏览器留出跳转和渲染的时间。为什么不用WebDriverWait因为登录跳转的目标URL每次启动时可能不一样用显式等待反而要额外处理条件轮询更直观。参数说明120秒是登录超时上限如果你扫码太慢或者网络卡顿超时后脚本不会崩但Cookie文件不会生成你只需要重新跑一次。判断跳转用的是login关键字这个条件在12306的登录流程里是稳定存在的从登录页跳到个人中心后URL会有明显变化如果你发现自己的环境不满足条件可以用driver.find_element(By.ID, qd_index)检测首页订单按钮是否存在来替代。Cookie文件生成后每次启动脚本前先加载它def load_cookies(driver): with open(COOKIE_FILE, r, encodingutf-8) as f: cookies json.load(f) for c in cookies: driver.add_cookie(c) driver.refresh()这里有个细节driver.add_cookie()必须在目标域名下调用也就是说你得先driver.get(https://kyfw.12306.cn)打开过一次页面域名确认了才能往里塞Cookie不然会报“cannot add cookie for non-current domain”。这是新手必踩的坑之一。3.2 余票查询解析station_name.js的站名编码12306的余票查询URL格式大致是https://kyfw.12306.cn/otn/leftTicket/query参数需要出发站编号、到达站编号、日期、坐席类型。这里的站名编码不是汉字也不是拼音而是车站数据文件station_name.js里的固定编号。这个文件在12306的静态资源目录里可以直接拉取内容是var station_names c|BJP|北京北|beijingbei|bjb|...这种结构。解析它其实是整个脚本里最“算法”的一步。我的做法是在启动时加载一次解析成字典后续查询直接查表——做起来不复杂但容易在中文名和拼音的对应上踩坑城市有同名站、有带“站”字和不带“站”字的输入差异统一以中文名为准。import re import requests class StationDB: def __init__(self, js_url): self.name2code {} self.code2name {} self.parse(js_url) def parse(self, url): r requests.get(url, timeout10) text r.content.decode(utf-8) # 字段以 c| 和 | 分隔顺序为 拼音缩写|站名|电报码|拼音|简拼|编号 for item in text.split()[1:]: parts item.split(|) if len(parts) 6: code parts[5] name parts[1] self.name2code[name] code self.code2name[code] name stations StationDB(https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version1.9300) print(stations.name2code[北京]) print(stations.name2code[上海虹桥])逻辑说明代码拉取station_name.js按分割得到每个车站的独立条目再按|分割字段取第2个中文名和第6个编号构建正向和反向映射。这样查询时传入“北京”直接拿到编号拿到响应后也可以根据编号反查站名来校验结果。station_version这个查询参数不是固定的它会随文件更新变化但你用不带版本号的原始URL也能拿到最新文件所以不用精确到版本号。参数说明requests.get的timeout10约束了请求上限超过10秒直接抛异常让脚本终止避免网络抖动导致挂死。正则也可以用但我更推荐字符串分割直观且不容易错。解析后建议打印两个站名做冒烟验证比如“北京”和“上海虹桥”确认字典结构没问题再继续。余票查询本身用HTTP请求就能完成不一定非得走Selenium的浏览器渲染——这也算Selenium抢票脚本的另外一个思路用Selenium管登录和提交用requests管高频查询。查询请求带好Cookie响应是JSON格式里面包含了每个车次的余票状态码比如“有票”用数字表示“无票”用空串表示。判断逻辑不复杂核心是查一次、解析一次、循环判断。3.3 提交订单从“预定”到“提交订单”的节点控制查询到有票之后脚本要接管浏览器去执行真正的下单操作。这一步必须走Selenium原因是下单接口的加密参数依赖页面环境你手动用requests伪造请求十有八九会被服务器拒绝。流程是定位到符合条件的车次行点击“预定”按钮等弹窗加载出乘车人列表勾选乘客点“提交订单”最后在确认页面点“确定”。每一步之间必须有等待条件不能只靠time.sleep(1)裸奔——网络慢的时候页面元素还没渲染完你就去点击直接抛NoSuchElementException。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By def select_train(driver, expect_train_no): # 等待车次列表渲染完成 wait WebDriverWait(driver, 15) rows wait.until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, #queryLeftTable tr)) ) for row in rows: try: # 车次号在每行的第 1 列 no row.find_element(By.CSS_SELECTOR, td:nth-child(1)).text.strip() if no expect_train_no: # 等待“预定”按钮变为可点击 btn row.find_element(By.CSS_SELECTOR, a.btn72) if btn and 预定 in btn.text: btn.click() return True except Exception: continue return False逻辑说明代码先等车次表格的行元素全部出现然后遍历每一行提取第一列的文本作为车次号匹配目标车次一旦命中就点击该行的“预定”按钮。try/except包裹单行解析是必要的因为表格里有些行是分隔行、有些行是高铁动卧的特殊结构与其写复杂的判断逻辑不如让异常跳过不可用的行。这里用了显式等待而不是固定sleep好处是页面加载快慢都能自适应。参数说明15秒是等待车次列表出现的最大时长超过会抛TimeoutException你要根据实际网络环境调这个值。td:nth-child(1)是列选择器12306的表格列顺序调整时这里要跟着改。btn72这个class是12306车次行“预定”按钮的稳定样式名但不是官方对外接口页面改版时可能失效——遇到失效不要慌用浏览器F12看新按钮的class再替换即可。点完“预定”后页面会弹出确认订单的窗口这时视野要切到新弹出的dialog元素再依次勾选乘车人、提交订单。这一段和上面逻辑类似不再重复贴代码但有一个决定性的参数值得单独说乘客选择的等待时间。弹窗里的乘车人列表是从后台异步拉取的直接点击很容易扑空建议配合EC.element_to_be_clickable来点击每个乘客的复选框。如果乘客名单比较长优先用身份证尾号做定位关键词别用姓名重名会勾错人。走到“提交订单”这一步抢票脚本的“技术含量”基本用完了接下来拼的是稳定性和容错。而稳定性的核心是参数调得够不够细。4. 抢票参数到底怎么调刷新间隔、加载策略与超时控制写一套脚本跑通与让一套脚本稳定跑完一轮起售这是两码事。我见过很多人在第一个版本里把查询间隔设成0.1秒结果跑了两分钟IP被临时限制后面所有请求全部超时。这章把几个关键参数讲透讲完你就知道为什么有些抢票脚本“玄学”般好用有些跑一次就封。4.1 刷新间隔不是越快越好查询压力与IP风控12306的余票接口对频率是有限制的具体阈值没有官方口径但根据社区长期的观察单IP密集请求持续一段时间后会出现连接重置或请求被导向验证页。所以刷新间隔的取值范围建议在13秒之间起售高峰可以降低到0.8秒但一旦出现连续超时就要立刻拉长退避。间歇性刷新比连续刷新更稳乱拳打死老师傅的打法在这里不适用。参数设计的核心是“指数退避”。维护一个连续失败计数器初始查询间隔1秒每失败一次间隔翻倍最多加到10秒一旦成功间隔重置回初始值。用伪代码表示就是interval 1.0 fail_count 0 while not bought: try: resp query_left_ticket(...) fail_count 0 interval 1.0 handle_response(resp) except RequestError: fail_count 1 interval min(1.0 * (2 ** fail_count), 10.0) time.sleep(interval)逻辑说明2 ** fail_count实现了翻倍退避连续失败3次间隔就是8秒基本能缓解风控的紧张状态。min(..., 10.0)给间隔封顶防止长时间卡在超长等待里错过起售窗口。注意退避只针对查询接口一旦进入提交订单环节这个循环就退出了不能让订单提交也按1秒一次的频率跑高并发抢票的订单提交本来就容易碰上排队提示提交本身要的是快、准频率问题反而是次要的。4.2 页面加载策略与超时参数让浏览器不傻等Selenium打开的浏览器默认会等待页面所有资源加载完毕才返回控制权这对抢票场景是灾难——12306首页挂了几个大图和一个慢速统计脚本光等它就浪费好几秒。正确的做法是把页面加载策略改成none或eager让浏览器不等次要资源交互元素一就绪就继续执行。这个参数能省掉你宝贵的起售时间。from selenium import webdriver options webdriver.ChromeOptions() # eagerDOM 加载完成后立即继续不等样式表和图片 options.page_load_strategy eager # 或者更激进none完全不等 # options.page_load_strategy none driver webdriver.Chrome(optionsoptions) driver.set_page_load_timeout(15) driver.set_script_timeout(10) driver.get(https://kyfw.12306.cn/otn/leftTicket/init?linktypeiddc)参数说明eager和none的区别是让出控制权的时机eager是DOM树解析完成就返回适合大部分场景none是发出导航请求就返回页面的很多元素这时候还没生成你的后续查找操作必须用显式等待硬扛。说实话none更适合配合CDP做精细控制的场景抢票脚本用eager性价比最高既不等图片又保证关键按钮可用。set_page_load_timeout和set_script_timeout是双保险防止个别资源挂起拖死整个会话。4.3 12306 MCP和“改接口”思路Selenium之外的选择写到这里插一句最近圈子里讨论比较多的事情有开发者尝试把12306的余票查询接入MCP模型上下文协议让大模型代理去调用余票接口替代手写循环轮询。这个方向的吸引人之处在于用自然语言就能编排抢票策略比如“帮我盯今天到上海的高铁二等座有票就下单”。但从落地效果看MCP方案要解决的核心问题和Selenium方案一样登录态。查询接口可以做成工具给AI调用但真正提交订单还是绕不开网站的加密参数和风控。所以我的判断是MCP目前更适合做余票监控和票情分析离“自动买到票”还有距离——至少现阶段Selenium这类浏览器自动化方案依然是落地的稳定底线。4.4 提交订单阶段的两个关键参数重试开关与排队提示提交订单时页面可能出现两种状态一是弹“当前排队人数过多”二是按钮置灰不可点击。这两种情况的处理参数完全不同。排队提示出现时不要关闭弹窗重来排队本身就是状态机——系统给了一个排队序号你在队列里等着刷新就意味着退出重排。你应该做的是轮询检查排队提示是否消失超过30秒再考虑放弃重试。按钮置灰则是另一回事说明当前车次已经无票或者你提交的乘客类型与该坐席不匹配这时候别傻等返回查询循环重新找下一轮放票。把这个判断逻辑写进参数配置里能让脚本在起售高峰期更“抗造”。def submit_order(driver, max_queue_wait30): wait WebDriverWait(driver, 5) submit_btn wait.until(EC.element_to_be_clickable((By.ID, submitOrder_id))) submit_btn.click() # 检测排队框可选配置项排队超时时间 start time.time() while time.time() - start max_queue_wait: queue_bar driver.find_elements(By.CSS_SELECTOR, #queue_div) if queue_bar and queue_bar[0].is_displayed(): time.sleep(1) continue # 排队结束进入确认环节 confirm_btn driver.find_element(By.ID, qr_submit_id) confirm_btn.click() return True return False逻辑说明点击“提交订单”后代码进入一个时间窗口型的循环只要排队div可见就持续等待排队div消失就立刻点击最终确认按钮。max_queue_wait30意味着排队最多容忍30秒超时放弃并返回False让上层逻辑决定是继续查询还是暂停一会儿。这里的time.sleep(1)是排队轮询间隔不用设太短因为排队状态的变化是以秒为粒度的设成1秒已经具备不错的响应速度。参数说明#queue_div是12306排队提示框的常见选择器但我要强调一下页面的ID不是永久不变的你要是发现选择器失效优先打开浏览器开发者工具确认一下实际结构而不是怀疑自己代码写错了。qr_submit_id是最终确认按钮的ID在改版中相对稳定。max_queue_wait的值要根据抢票时段调整起售高峰排队常见30秒以上你可以调到60但不要无限等非高峰时段通常秒过30秒绰绰有余。5. 抢票脚本的避坑清单5个高频翻车点与排查办法这一章是血泪经验的汇总。写这套脚本的人多踩坑的人更多下面5个问题基本覆盖了绝大多数新手会碰到的情况按“现象→原因→解决”的顺序列出来你遇到问题时可以直接对号入座。5.1 启动就报“SessionNotCreatedException”现象脚本启动后立刻崩溃报错内容里带着“This version of ChromeDriver only supports Chrome version XXX”。原因ChromeDriver和本机Chrome版本不匹配浏览器自动更新过但驱动还是旧版。这是环境问题里最常见的占了这类报错的八成。解决去ChromeDriver镜像站下载与本机Chrome大版本一致的驱动替换掉原来的二进制文件。如果机器上有多个Chrome版本还要留意系统PATH指向的是哪一个。另外Selenium 4.x自带的Selenium Manager有时会拉错驱动手动指定service webdriver.ChromeService(executable_path/path/to/chromedriver)能绕开自动匹配的坑。版本对齐这件事没有捷径属于一次配置长期受益的基础工作。5.2 页面能打开Cookie也保存了但脚本一跑就跳到登录页现象手动登录没问题脚本启动后加载Cookie也显示成功但执行到查询时又被重定向回登录页。原因Cookie过期或者Cookie的域名/路径不完整。12306的Cookie里有几个关键会话字段有效期比较短而且有些字段是HttpOnly的你保存的是浏览器当前会话的快照不代表永久有效。另外如果你用--user-data-dir加载了旧用户目录目录里的旧Cookie可能和新保存的Cookie互相覆盖。解决删除本地Cookie文件重新走一遍扫码登录流程确保登录后立刻保存到新的文件。加载Cookie时不要用driver.add_cookie一条条塞而应该先打开登录页建立会话域再塞Cookie并刷新页面。建议把Cookie文件按日期重命名比如cookies_20250101.json方便回退排查。如果还有问题检查系统时间是否准确——Cookie校验对时间敏感系统时间偏差大会导致会话直接判定失效。5.3 车票显示有票点击提交却提示“余票不足”现象查询接口明明返回了有票状态点进提交订单环节却被提示没有余票也就是网上很多人说的“12306显示有票买的时候又显示余票不足”。原因这是两个层面的数据一致性延迟。查询接口的余票状态本身就存在数秒缓存页面显示“有票”不代表那几张票还在更关键的是余票锁定发生在你点击提交订单之后别人比你早几十毫秒提交票已经被锁走你晚了一步就锁不到。这不是脚本的bug是并发竞争的正常结果。解决这种情况说明你的查询频率和提交速度还有提升空间。第一缩小查询范围只查目标车次别全页面扫描减少决策耗时。第二把提交订单的代码路径优化到最短去掉多余的等待条件。第三接受一个事实在极端热门的线路上脚本只是提高概率不是保证成功。遇到这种提示脚本应该立即返回查询循环等下一轮而不是卡在错误状态里空转。5.4 起售时间一到浏览器一直在转圈不出结果现象起售那一刻点“查询”浏览器顶部标签一直在转等了十几秒才出数据等到能点击时票已经没了。原因页面加载策略选得太保守默认等待全部资源加载。12306的余票页面在起售瞬间会推入大量数据图片、脚本、统计SDK都会挤压带宽你闷头等浏览器加载完成后台的抢票请求早就把票耗光了。解决把页面加载策略改成eager并且对查询按钮的点击时机做优化——起售前先把查询条件填好、页面停在查询页起售时间一到直接点击“查询”不重新加载整个页面。还有一种常见做法是定时任务精准触发用Python的schedule库在起售时间前5秒启动脚本预留页面加载时间让查询请求刚好在起售时刻发出。另外把浏览器窗口最小化并不能提快速度反而某些系统会降低后台进程的调度优先级干脆让窗口保持前台别折腾。5.5 跑着跑着突然弹出滑块验证现象脚本连续运行一段时间后页面弹出滑块验证组件Selenium后续操作全部卡住直到超时。原因频率触发了风控。bytpass分流抢票为什么“被识别”是必然的——打着抢票名义的高频请求目标明确特征明显平台不拦你拦谁你用自己的Selenium脚本虽然没有第三方软件那么强的特征但长时间高频操作同一个接口同样会被识别。滑块本质上是平台在告诉你操作密度超出真人范围来验一下。解决第一降低轮询频率把间隔从0.5秒拉长到1.52秒虽然抢票概率会稍降但能保证脚本不被打断。第二设计人机协作流程检测到滑块组件出现时脚本发送桌面通知或播放提示音由你来手动完成滑块验证验证通过后脚本继续运行。自动识别滑块的方案不是不可行但12306的滑块轨迹校验越来越严自动化的通过率不稳定与其花时间调识别代码不如留一个人工兜底的口子。说到底平台设置验证码的本意就是防止自动化以现在的风控水平硬拼识别算法是个性价比很低的方向。6. 验证脚本的最终办法把抢票拆成三段来测试拿到别人的“全部资料详细文档”或者在本地写完这套脚本之后最该做的不是等下一次起售直接开跑而是先做一次演练。我的习惯是把流程拆成三段来测任何一段不过关都不进入下一段。第一段测登录启动脚本扫码登录保存Cookie退出再启动脚本加载Cookie刷新页面确认登录态还在。这段测试的价值在于排除所有Cookie和会话层面的问题而且随时能做不依赖车次时间。第二段测查询随便选一个非热门线路比如北京到石家庄跑10次查询循环观察响应时间、解析结果、退避逻辑是否正确。重点看两个指标平均响应时间是否在1秒内连续查询多次后是否出现超时或验证码。第三段测提交找一个有余票的普通车次把脚本流程走到“提交订单”但不要支付验证到确认订单页面就手动终止。这一步能检查从车次定位到乘客勾选再到订单确认的全链路是否通畅。有一个我个人的习惯分享给你在正式抢票前把车次方向换成反方向试一次——比如你要抢北京到上海就先跑一次上海到北京的查询提交流程。反向车次的余票通常充足更容易完整走通流程也更容易暴露提交环节的隐患。确认三段全通实战时你才有底气把脚本挂在那里去干别的事。另外建议每次实战前更新一次station_name.js的缓存12306偶尔会新增站点、调整编号用旧缓存查新站名会直接查不到票。这套方案的价值不只在抢票这件事本身。你把登录态管理、轮询退避、显式等待、页面加载策略这些细节吃透换成盯演唱会门票、验证码短信、秒杀场景底层思路都能复用。对我来说每次起售日蹲在电脑前看着脚本代替我做那些重复动作自己只负责处理验证码和最终支付那种“工具就位、逻辑闭环”的踏实感比抢到票本身更有成就感。希望你也能在调试这套脚本的过程中把Selenium从“听说过”变成“用得顺手”。好这套方案就讲到这希望帮到你。本文还有配套的精品资源点击获取
返回列表