简介:使用Selenium驱动的自动化购票工具,针对大麦网演出票务场景,帮助有购票需求的用户通过模拟登录、流程操作与自动提交,提升抢票成功率。压缩包整体约1.08MB,共18个文件,以8个Python脚本为核心,覆盖主程序、配置解析与会话维护等模块;另含JSON配置文件、PNG截图说明与流程图,方便对照调整参数。
资源已有6184人学习浏览,脚本由开发者实际验证可运行,适合有一定Python基础、希望二次开发或直接部署使用的用户。包内不仅提供完整代码,还附带环境配置说明、配置项示例与流程图示,可快速上手并依据个人账号信息修改config.json,实现定制化抢票策略。对于想了解Selenium自动化操作或票务系统交互逻辑的读者,也是一份不错的参考。
1. 大麦抢票脚本 V1.0:这不是玄学,是Selenium模拟购票的完整闭环
大麦抢票脚本 V1.0 这个项目,核心是用 Selenium 驱动真实浏览器,复刻用户在大麦网页端的完整购票动作:打开演出详情、扫码登录、选场次票档、添加观演人、提交订单。压缩包里的 README 附有验证成功的订单截图,说明它不是躺在仓库里吃灰的 demo,而是已经被跑通过的方案。适合两类人:一类是想研究大麦网页自动化流程的测试工程师和爬虫工程师,另一类是确实有演出票要抢、愿意花半小时配置环境的普通用户。更难得的是,包内同时给了 PC 端 Selenium 和 damai_appium 移动端两条路径,前者适合网页场景,后者在 App 上兜底,比单一方案扛得住现场翻车。
2. 先盘清楚项目结构:从zip解压到环境就绪的30分钟
2.1 文件树里藏着两个方案:PC浏览器和Appium
解压 damai_grab_votes-main.zip 后,第一件事不是双击 README,而是先把目录结构过一遍。这个项目的组织方式很直白,核心代码按运行环境拆成了两个包:
damai_grab_votes-main/ ├── .DS_Store ├── damai/ │ ├── __init__.py │ ├── concert.py │ ├── requirements.txt │ ├── config.json │ ├── damai.py │ └── config.py ├── damai_appium/ │ ├── __init__.py │ ├── damai_app.py │ ├── damai_app_qiang.py │ ├── config.json │ └── config.py ├── doc/ │ ├── 大麦抢票流程.drawio │ └── 大麦抢票流程.png ├── img/ │ ├── example.png │ ├── example_detail.png │ └── config_json.png └── README.mddamai/ 是 PC 端方案,damai.py 是主入口,concert.py 封装了演出场次相关的数据结构,config.py 负责读取 config.json 并转成 Python 对象。damai_appium/ 是移动端方案,damai_app.py 处理 App 内的启动、登录和页面跳转,damai_app_qiang.py 专门做开票瞬间的循环抢点。doc/ 里的 drawio 是流程图源文件,png 是导出的静态图;img/ 里三张示例图分别对应整体效果、购票细节和 config.json 填写效果。
我建议第一次接触的人只动 damai/ 这个目录,PC 端跑通了再去看 damai_appium/。因为两条路径的依赖和调试方式完全不同,同时开工容易搞混。doc/ 下的流程图值得先打开看一眼,它会告诉你整个抢票链路分几步:进入详情页 → 登录 → 等待开票 → 选择票档 → 提交订单。后面所有代码都是围绕这几步展开的。
2.2 Python环境与依赖安装:requirements.txt不是摆设
Windows 上安装 Python 3.9 以上的版本时,最容易被忽略的是安装向导第一页的 "Add Python X.X to PATH" 复选框。这个不勾,命令行里敲 python 会直接报"不是内部或外部命令"。装完后在命令提示符里验证一下:
python --version能正常输出版本号,再进到 damai 目录装依赖:
cd damai python -m pip install -r requirements.txtrequirements.txt 里通常至少包含 selenium 和 webdriver-manager 这两项。selenium 负责驱动浏览器,webdriver-manager 负责自动下载与本地 Chrome 版本匹配的 chromedriver,省去手动配 driver 路径的麻烦。如果你用的网络环境没法走 pypi 默认源,可以加-i https://pypi.tuna.tsinghua.edu.cn/simple切换镜像源,血泪经验,不然卡在下载依赖这一步半小时很常见。
装完之后建议顺手确认一下 selenium 版本,常见做法是用pip show selenium看,只要不低于 4.x,接口基本都能对上。这个脚本用的是老牌 API 写法,也就是webdriver.Chrome()加find_element_by_*那套,如果你装的是 selenium 4.6 以上版本,部分写法会提示弃用但不影响运行,先不用管。
2.3 config.json参数说明:场次、票价、观演人
config.json 是整份资源的"总开关",脚本启动后第一件事就是读它。压缩包的 img/config_json.png 展示的就是这个文件的填写效果,我按常见字段给你拆一张参考,实际以包内文件为准:
{ "target_url": "https://detail.damai.cn/item.htm?id=xxx", "session_time": "2025-06-01 19:30", "price": 580, "buyer_name": "张三", "id_card": "110101199001011234", "login_method": "qr", "headless": false, "max_retry": 30, "interval": 0.2 }逐个说参数逻辑。target_url是演出详情页链接,脚本会在这个页面里等待"立即购买"按钮从置灰变可点。session_time要精确到分钟,建议和演出详情页标注的开票时间完全一致,精确到秒反而容易被字符格式卡住。price是你想抢的票档价位,脚本找到对应按钮后按价位过滤,避免误点成看台票。buyer_name和id_card是观演人信息,大麦下单前会要求核对实名信息,提前填好能省掉选人那一两步。login_method支持扫码和账号密码两种,qr 是最稳的。headless控制浏览器是否无头运行,调试阶段务必设成 false,亲眼看页面才能定位问题。max_retry和interval分别是最大轮询次数和每次刷新间隔,默认 30 次、0.2 秒是个相对保守的组合,不会把页面刷崩。
我第一次用这个脚本时栽在session_time上,填了2025-06-01 19:30:00,带秒的格式在部分版本里解析异常,按钮一直检测不到。去掉秒之后一切正常。所以提醒一句:字段格式严格遵守示例,不要自己加单位或补零。
3. Selenium抢票链路:登录态、场次票档与提交订单的细节
3.1 为什么选Selenium而不是直接requests
很多人第一反应是:抢票不就是 POST 一个下单接口吗,用 requests 直接构造请求不是更快?我也这么试过,但大麦的页面里藏了几层逻辑:按钮置灰状态由前端 JS 控制,开票那一刻接口参数里会带上加密的 token,而且高频请求会触发滑块验证。requests 模拟这些交互,等于把前端加密逻辑全部逆出来,工程量比写自动化脚本大得多。
Selenium 的价值在于它驱动的是真实浏览器,页面执行什么 JS、生成什么 token、弹出什么验证,都由浏览器自己完成,脚本只是模拟"人"的操作节奏。缺点也明显:慢、吃内存、并发能力差。但抢票场景下,慢换来的稳定是值得的,尤其当你只盯一个场次的时候,Selenium 的胜率远高于盲猜接口。这个项目的 damai.py 走的正是这条路,用真实 Chrome 完成从进入详情页到点击提交的全过程。
3.2 登录流程:扫码与cookie复用
第一轮运行时,脚本会打开大麦登录页,等你用 App 扫码。登录成功后,selenium 拿到的 session 里会带着完整的 cookie,这部分代码逻辑是这样写的:
from selenium import webdriver import json, time driver = webdriver.Chrome() driver.get("https://passport.damai.cn/login") input("扫码登录完成后,回到这里按回车继续...") # 把当前会话的cookie落盘,后续启动直接复用 cookies = driver.get_cookies() with open("cookies.json", "w", encoding="utf-8") as f: json.dump(cookies, f) print(f"cookies 已保存,共 {len(cookies)} 条")这段代码的逻辑很直白:先打开登录页,用input()阻塞进程,等你扫码完成后敲回车;随后get_cookies()把会话里所有 cookie 取出来,json.dump写入本地文件。第二次运行就不需要再走登录流程,脚本启动时直接读 cookies.json 注入浏览器即可。这里的关键参数是保存路径,我建议把 cookies.json 放在和 damai.py 同级的目录,避免脚本里相对路径引用出错。
cookie 复用是这套方案能不能持久跑的核心。大麦的登录态一般能维持几个小时到一天,但注意:如果脚本被风控中途踢下线,cookie 文件还是旧的,会导致后续所有请求都停留在未登录状态。我一般会在脚本开头加一个登录态检查,跳转到"我的订单"页面看是否被重定向到登录页,是的话就重新走扫码流程。
3.3 购票主循环:按钮置灰检测与点击时机
抢票的核心不是"点击"这个动作,而是"等待可点击"这个状态切换。开票前按钮是置灰的,开票瞬间前端 JS 把状态改掉,脚本要做的就是高频轮询这个状态变化。damai.py 的购票段大致是这样一个循环:
from selenium.webdriver.common.by import By import time max_retry = 30 interval = 0.2 buy_btn = None for i in range(max_retry): try: # 定位购买按钮,大麦PC端的购买按钮class名通常是buy-link buy_btn = driver.find_element(By.CLASS_NAME, "buy-link") if buy_btn.is_enabled(): buy_btn.click() print(f"第{i+1}次轮询检测到按钮可点击,已触发") break except Exception: pass driver.refresh() time.sleep(interval)逻辑说明:每次循环先按 class 定位购买按钮,is_enabled()判断是否可用;如果按钮还置灰,就刷新页面再等一个interval。这里max_retry是重试上限,interval是刷新间隔。两者组合决定了脚本的激进程度——间隔 0.2 秒意味着 30 次轮询只覆盖 6 秒窗口,开票瞬间服务器压力大,页面加载可能超过这个窗口。我实际使用中会把max_retry调到 50 到 100,间隔保持 0.2 到 0.3 秒,太频繁刷新会加大被风控的概率。
点击之后脚本并不会停下来。订单确认页会出现票档列表和观演人信息,这里需要用 config.json 里的price字段做过滤,选中对应票档后再提交订单。提交这一步最容易出岔子的是观演人勾选,很多版本会漏掉这一步导致订单卡在信息确认页,你在调试时盯着浏览器看它有没有自动勾选就行。
3.4 浏览器实例配置:隐藏自动化特征
selenium 驱动的 Chrome 默认会暴露navigator.webdriver属性,大麦这类网站对自动化工具的检测很敏感,一检测到就可能给你弹滑块验证。damai.py 里配置浏览器实例时,常见做法是这样处理:
from selenium import webdriver options = webdriver.ChromeOptions() options.add_argument("--disable-blink-features=AutomationControlled") options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option("useAutomationExtension", False) driver = webdriver.Chrome(options=options) driver.execute_cdp_cmd( "Page.addScriptToEvaluateOnNewDocument", {"source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"}, )前两行的作用是去掉 Chrome 右上角的"正在受自动化软件控制"提示条,第三行通过 CDP 协议在页面加载前覆写navigator.webdriver属性,让它返回 undefined。这套组合是当前绕过前端自动化检测的常见配置,但不保证永远有效,因为大麦的风控策略会动态调整。
headless这个参数也要在这里对应起来。无头模式能省内存,但更容易被识别,而且你调试时看不到页面状态。我的建议是:调试期永远用有头模式,确认整个流程稳定后,再考虑切无头跑。这个脚本的默认 config.json 里如果headless是 false,先不要改成 true。
4. damai_appium移动端方案:PC端被风控时的备选路径
4.1 Appium与Selenium的差异
damai_appium/ 提供的是一条完全独立的路径:通过 Appium 驱动手机上的大麦 App,而不是浏览器。为什么需要备选方案?因为 PC 网页端的风控策略通常比 App 端严格,而且有些演唱会场次只在 App 端开放购买。Appium 的远程驱动协议和 Selenium 同源,但控制对象从 Chrome 变成了 Android 上的 App 组件。
二者的差异主要在三个地方:一是元素定位方式,网页用 CSS 选择器或 XPath,App 用 resource-id 或 Android 专属的定位策略;二是执行环境,Appium 需要一个 Appium Server 监听 4723 端口,再配合 Android 模拟器或真机;三是稳定性,App 端页面渲染和网络请求更接近真实用户,风控阈值相对宽松。damai_appium 这套代码的价值就在于,PC 端被限制时你还有一条路可以切。
4.2 移动端config.json与元素定位策略
damai_appium/config.json 的字段和 PC 端长得不一样,它需要的是设备信息和 App 包名:
{ "appium_server": "http://127.0.0.1:4723/wd/hub", "platform_name": "Android", "device_name": "emulator-5554", "app_package": "cn.damai", "app_activity": ".homepage.MainActivity", "target_url": "https://detail.damai.cn/item.htm?id=xxx", "price": 580 }appium_server是 Appium 服务的地址,默认 4723;device_name填adb devices列出的设备序列号,模拟器通常是 emulator-5554;app_package和app_activity是大麦 App 的包名和启动 Activity,这两个值需要提前用adb shell pm list packages和adb shell dumpsys window查一次,不同版本的大麦 App 可能不一样。
元素定位是移动端方案里最玄学的部分。网页端的 class 和 id 相对稳定,App 端每次版本更新都可能改 resource-id。damai_app_qiang.py 里定位购买按钮常见写法是:
from appium import webdriver from selenium.webdriver.common.by import By caps = { "platformName": "Android", "deviceName": "emulator-5554", "appPackage": "cn.damai", "appActivity": ".homepage.MainActivity", "noReset": True } driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps) # 优先用resource-id定位,找不到再退回xpath try: buy_btn = driver.find_element(By.ID, "cn.damai:id/buy_btn") except Exception: buy_btn = driver.find_element(By.XPATH, "//android.widget.TextView[contains(@text, '立即购买')]")这里noReset很关键,设为 True 表示启动 App 时不清空登录态,否则每次启动都要重新过一遍登录流程。定位策略上我一般优先 resource-id,因为它稳定且定位快;只有 id 找不到时才用 XPath 按文本匹配兜底。contains(@text, '立即购买')这种写法能扛住部分 UI 调整,但文本一旦改动就需要同步更新脚本。
4.3 damai_app.py和damai_app_qiang.py的分工
这两个文件的职责分得很干净:damai_app.py 负责"进场",damai_app_qiang.py 负责"开枪"。damai_app.py 里做的是启动 App、检查登录态、通过 deep link 或driver.get(target_url)跳到演出详情页;damai_app_qiang.py 则专注做轮询点击,核心逻辑和 PC 端一样,只是元素定位换成 App 端的 id。
这个拆分的实际意义是,你可以先单独跑 damai_app.py 确认登录和跳转正常,再跑 damai_app_qiang.py 进入抢票循环。排查问题时不至于两个环节混在一起。如果 damai_app.py 卡在登录页,多半是noReset没生效或 App 内登录态过期;如果它正常跳转了但 qiang 脚本点不到按钮,大概率是 resource-id 变了,重新用uiautomatorviewer抓一次元素。
5. 避坑与排查:抢票脚本最常见的5个翻车现场
5.1 本地时间比服务器慢,开抢永远慢一拍
现象:脚本启动后按钮一直置灰,开票时间过了 10 秒才突然检测到可点击,点击后订单已经售罄。
原因:你的系统时间和服务器时间存在几秒偏差,你看着本地 19:30:00 启动了脚本,实际服务器已经 19:30:04。大麦的按钮状态切换以服务器时间为准,本地时间滞后时,脚本轮询启动就比别人晚了。
解决:先查准时间再启动脚本。Windows 上用w32tm /stripchart /computer:ntp.aliyun.com看一眼本地与标准时间的偏移,偏差超过 1 秒就先同步时间。我一般会在脚本启动前加一个时间校准步骤,用 Python 请求一次国家授时中心的时间戳再对时,把偏移量打印出来,做到心里有数。
5.2 扫码登录后cookie没过多久就失效
现象:第一次扫码登录成功后一切正常,过两小时再跑,脚本停留在登录页不动,cookie 失效了。
原因:大麦的登录态分两种,一种是短时效的会话 cookie,另一种是记住登录状态的持久 cookie。脚本只保存了会话 cookie,没有把login_method相关的记住登录态参数一并保存。
解决:保存 cookie 时把整个 cookie 列表原样 dump,不要手动过滤;同时确认 config.json 里login_method是否为 qr。如果失效频率还是高,可以检查 cookies.json 里是否包含名为damai.cn域下的 token 类字段,没有的话重新扫码生成一次。我自己的习惯是开场前一小时跑一次登录流程,用当前的新鲜 cookie 去等开票,不提前一天就把 cookie 准备好。
5.3 无头模式下按钮点得到却总在下单页报错
现象:脚本在 headless 模式下轮询正常,点击按钮也成功,但订单确认页始终加载不出来,报超时。
原因:无头模式不渲染部分异步组件,大麦订单确认页的票档列表和观演人信息是通过 JS 异步加载的,无头模式下这些请求优先级被降级或者被风控拦截。
解决:抢票场景不要开无头模式。如果确实需要降低资源占用,把 Chrome 窗口最小化而不是无头运行,窗口最小化不改变渲染行为,只是不占屏幕空间。我踩过这个坑之后,所有抢票脚本一律强制有头模式,宁可多耗 200MB 内存,也不赌无头渲染的兼容性。
5.4 高频率刷新触发滑块验证
现象:脚本跑着跑着,浏览器弹出滑块验证,页面卡在验证码处,轮询停摆。
原因:interval太短,刷新频率超过了风控阈值。大麦对同一 IP 下的页面刷新有频控,0.1 秒一次连续刷几十次,IP 就会被标记。
解决:把刷新间隔拉到 0.3 秒以上,并且不要每次 refresh 都重新加载整个页面。更优雅的做法是只刷新按钮区域所在的 DOM 节点,或者用 AJAX 轮询替代整页刷新。damai.py 的默认间隔 0.2 秒是个临界值,如果你所在网络环境 IP 比较干净还好,共享 IP 或机房 IP 建议直接改到 0.4。
5.5 提交订单按钮点击无反应
现象:按钮可点,脚本也触发了 click(),但页面没有任何变化,订单没有提交。
原因:页面里可能存在多个元素匹配同一个 class,click 点到了隐藏的蒙层元素上,或者按钮上有 JS 事件绑定的 debounce 逻辑,快速点击被吞掉。
解决:定位元素时加上可见性过滤,用 selenium 的is_displayed()先判断元素是否真正渲染出来,再执行click()。另外,点击后要主动等待页面跳转,等待条件设置为某个订单号元素出现,而不是硬等固定秒数。我处理过最多的案例就是 click 没打中元素,换成driver.execute_script("arguments[0].click();", el)强制触发,反而更稳。
6. 验证与进阶:先跑演练模式再碰真实场次
拿到这份资源后,最忌讳直接拿一场热门演唱会的开票时间做实验。我的习惯是先花 20 分钟做一轮完整演练:选一场已经开售、库存充足的演出,把 config.json 里的 target_url 换成这个场次,session_time 填当前时间,跑一遍全流程。这一步能验证三件事:登录态是否正常、元素定位是否还匹配、订单提交链路是否完整。
验证跑通后,再针对真实场次做调优。我推荐一个成本最低的进阶操作:开票前 5 分钟启动脚本,让浏览器停在详情页并完成登录检测,这样开票瞬间不需要重新加载页面,比临时启动浏览器快一个身位。具体做法是在 damai.py 里加一个预热参数,启动后先跳转详情页等待,不进入轮询循环,等系统时间到达设定值前 5 秒再切到轮询模式。
另外建议把运行日志保存下来,不要只在控制台打印。每次抢票结束后翻日志,看首次可点击检测到的时间点和服务器开票时间的差值,这个差值就是你脚本的实际延迟。我之前一直以为自己的脚本 0.5 秒内能响应,翻了日志才发现平均要 1.8 秒,原因就是每次轮询都整页刷新花费了时间。改成局部刷新后延迟稳定在 0.6 秒左右。
还有一个容易忽略的验证项:多账号并发。如果你有多张票需求,建议在 Docker 或独立虚拟环境里分别跑实例,共用一个 config.json 会互相踩配置文件。每个实例单独建目录,复制整套资源进去,各自维护自己的 cookies.json。从那以后,我每次拿到新的抢票脚本都会强制走一遍这套流程——先演练、再预热、再查日志、最后才上真实场次。希望帮到你。
本文还有配套的精品资源,点击获取