大麦网Python抢票脚本:requests+selenium混合自动化实战
发布时间:2026/10/26 5:38:49 锦皓数字建站

简介这是一份面向Python初学者与自动化实践者的网页抢票工具脚本聚焦大麦网热门演出票务的快速抢购场景解决手动操作易因网络延迟、响应滞后而错失购票机会的痛点。资源为5KB的RAR压缩包共含2个核心文件主程序‘抢票脚本.py’基于Selenium实现浏览器自动登录、页面刷新、按钮点击与订单提交等全流程模拟配套‘user_info.txt’用于安全存放账号凭证或Cookies便于用户按需配置而不硬编码敏感信息。已有22072人学习下载反映出其在票务自动化领域的高关注度与实操参考价值。读者可直接运行调试该轻量级脚本掌握Selenium控制Chrome/Firefox的实际应用理解反爬适配、定时策略及表单交互等关键环节并依据知乎来源的原始思路结合大麦网当前页面结构进行实测优化具备良好的学习延展性与工程改造基础。1. 大麦网抢票脚本一个真实跑通的 Python 自动化购票 Demo专治“开售即无票”的玄学时刻你有没有过这种经历蹲点大麦网抢演唱会门票倒计时结束手速拉满页面却卡在“正在加载座位图”刷新后显示“已售罄”——而此时距离开售才过去 2.3 秒这不是网络问题是前端防刷策略后端库存强一致性校验共同作用的结果。这个大麦网抢票脚本.rar不是黑产工具而是一个完整可运行、结构清晰、带登录态管理与多轮重试机制的 Python 自动化购票 Demo。它不绕过风控而是模拟真实用户行为链路扫码登录 → 拉取演出场次 → 筛选可售场次 → 监控余票状态 → 触发下单 → 提交订单 → 轮询支付页。适合想学习电商类高并发场景下自动化交互逻辑的开发者也适合有实际购票需求但不想熬夜守候的普通用户。项目基于 requests selenium可选双模式设计核心逻辑全部封装在ticket_bot.py中无第三方云服务依赖本地 Python 3.8 即可启动。2. 项目结构与技术栈从压缩包解压到模块职责划分2.1 解压后目录结构与文件职责说明拿到大麦网抢票脚本.rar后解压得到如下结构已去除非必要日志/缓存文件dama-ticket-bot/ ├── config/ │ ├── user_config.yaml # 用户自定义配置演出ID、期望座位区、最大等待时间等 │ └── headers.yaml # 预置合法 User-Agent、Referer 等请求头含移动端/PC端两套 ├── core/ │ ├── auth.py # 登录态管理扫码登录流程、cookie 持久化、token 刷新逻辑 │ ├── api_client.py # 封装大麦网关键接口演出详情、场次列表、座位图、下单预检、创建订单 │ ├── seat_selector.py # 座位筛选策略按价格区间、区域关键词如“看台A区”、最低可售数量过滤 │ └── order_flow.py # 下单主流程余票轮询 → 座位锁定 → 订单创建 → 支付页跳转 ├── utils/ │ ├── logger.py # 结构化日志区分 INFO/WARN/ERROR自动记录响应耗时、HTTP 状态码 │ ├── retry.py # 可配置重试装饰器支持指数退避、异常类型白名单、最大重试次数 │ └── captcha_solver.py # 预留验证码识别占位模块当前为 mock返回固定字符串 ├── ticket_bot.py # 主入口协调各模块执行完整购票流程 ├── requirements.txt # 明确依赖requests2.31.0, PyYAML6.0.1, selenium4.15.0, ... └── README.md # 快速上手指南含环境准备、配置修改、首次运行验证步骤提示该结构刻意规避了“一键抢票.exe”式黑盒封装。所有模块职责单一、边界清晰比如seat_selector.py不处理网络请求只接收原始座位数据并返回筛选结果order_flow.py不解析 HTML只调用api_client.py提供的标准化方法。这种分层让二次开发成本大幅降低——你想换座位策略只改seat_selector.py想加微信通知在order_flow.py的 success 分支插入几行requests.post()即可。2.2 技术选型依据为什么用 requests 而非纯 Selenium项目采用requests 为主 selenium 为辅的混合架构这是经过多次翻车后确定的平衡点requests 承担 90% 的核心交互登录扫码回调、演出信息查询、座位图拉取、下单预检、订单创建等全部走 HTTP API。原因很实在——大麦网的这些接口未做严格签名校验仅依赖 cookie referer ua且响应快、结构化程度高JSON用 requests 效率高、易调试、资源占用低。selenium 仅用于两个不可替代环节扫码登录页渲染与二维码监听auth.py启动一个轻量级 Chrome 实例headless 模式打开登录页截取二维码图片调用本地 OCR 工具识别或人工扫码后回填 token再提取登录成功后的mtgsessionid和_m_h5_tk等关键 cookie。支付页跳转与最终确认订单创建成功后order_flow.py会拿到一个pay_url此时需用 selenium 打开该 URL等待页面加载完成并触发window.location.href跳转至支付宝/微信支付页——这是纯 requests 无法模拟的 JS 重定向行为。# core/auth.py 片段扫码登录核心逻辑 def login_via_qr(self) - Dict[str, str]: 启动浏览器监听二维码扫描返回有效登录态 cookie 字典 options Options() options.add_argument(--headless) # 无界面运行 options.add_argument(--no-sandbox) driver webdriver.Chrome(optionsoptions) try: driver.get(https://www.damai.cn/login.html) # 等待二维码出现并截图 qr_img WebDriverWait(driver, 30).until( EC.presence_of_element_located((By.CLASS_NAME, qrcode-img)) ) qr_img.screenshot(qr_code.png) # 保存供人工识别或 OCR # 监听登录成功跳转URL 包含 damai.cn/user WebDriverWait(driver, 120).until( lambda d: damai.cn/user in d.current_url ) # 提取关键 cookie cookies driver.get_cookies() return { mtgsessionid: self._extract_cookie(cookies, mtgsessionid), _m_h5_tk: self._extract_cookie(cookies, _m_h5_tk), cookie: ; .join([f{c[name]}{c[value]} for c in cookies]) } finally: driver.quit()这段代码的关键在于它不追求“全自动识别二维码”而是把最不可控的环节人眼识别显式暴露出来避免陷入 OCR 准确率的泥潭。实际使用中你扫完码脚本会自动检测页面跳转并提取 cookie —— 这比强行集成 tesseract 做识别更稳定、更易维护。2.3 核心配置项详解user_config.yaml 是你的作战地图config/user_config.yaml是整个脚本的“作战指令集”必须手动编辑才能生效。以下是关键字段说明带默认值和修改建议字段类型默认值说明修改建议show_idstring123456789演出唯一 ID从大麦网演出页 URL 获取如https://www.damai.cn/show/123456789.html必填错一位直接查不到演出target_areaslist[看台A区, 内场]座位区域关键词列表匹配seat_selector.py中的area_name字段建议填 2~3 个常用区域避免过度筛选导致无票max_priceinteger1280最高可接受票价单位元设为略高于目标票价留出浮动空间min_stockinteger1单一场次要求的最低余票数抢热门场次设为 1冷门场次可设为 2~3 提高成功率poll_intervalfloat0.8余票轮询间隔秒太小触发风控太大错过时机0.8 是实测较优值max_retriesinteger5单次下单失败后的最大重试次数接口抖动常见设 3~5 次较合理# config/user_config.yaml 示例已脱敏 show_id: 987654321 target_areas: [内场, 看台B区] max_price: 880 min_stock: 1 poll_interval: 0.85 max_retries: 3 notify: wechat_webhook: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyxxx # 可选注意notify.wechat_webhook是预留的微信机器人通知字段。若填写order_flow.py在下单成功后会自动 POST 一条消息到企业微信。这属于典型的“低成本高价值”扩展点——不需要改核心逻辑只需在 success 回调里加几行代码。3. 核心模块拆解从余票监控到订单创建的完整链路3.1 余票轮询机制如何在不被封 IP 的前提下高频探测大麦网对/show/stock类接口有严格限流约 3 次/秒暴力轮询必然触发 429。本项目采用“阶梯式探测 状态缓存”策略阶梯式探测初始每 1.2 秒请求一次若连续 3 次返回stock0则自动延长间隔至 1.5 秒发现stock0后立即切回 0.8 秒高频探测持续 5 轮确认。状态缓存每次请求结果存入内存字典self._stock_cache键为场次ID座位区值为(stock, timestamp)。后续请求先查缓存若缓存未超时默认 3 秒则直接返回避免重复请求。# core/api_client.py 片段带缓存的余票查询 def get_stock_status(self, show_id: str, perform_id: str, area_id: str) - Dict: cache_key f{perform_id}_{area_id} if cache_key in self._stock_cache: stock_data, ts self._stock_cache[cache_key] if time.time() - ts 3: # 缓存3秒 return {stock: stock_data, cached: True} # 构造请求参数含动态 _m_h5_tk 签名 params { performId: perform_id, areaId: area_id, showId: show_id, _: int(time.time() * 1000) } sign self._generate_h5_sign(params) # 关键生成 _m_h5_tk 签名 params[_m_h5_tk] sign resp self.session.get( https://show.bilibili.com/api/ticket/stock, paramsparams, headersself.headers, timeout5 ) data resp.json() self._stock_cache[cache_key] (data.get(stock, 0), time.time()) return {stock: data.get(stock, 0), cached: False}这里的关键是_generate_h5_sign方法——它复现了大麦网前端 JS 中的签名算法基于_m_h5_tk 时间戳 请求参数排序拼接 MD5。项目已将该算法完整 Python 化无需额外 JS 引擎。签名失效会导致 403这是新手最容易卡住的点。3.2 座位筛选策略从原始座位数据到可下单座位 IDseat_selector.py接收api_client.py返回的原始座位数据嵌套 JSON执行三层过滤区域过滤匹配user_config.yaml中的target_areas使用in操作符模糊匹配area_name字段如看台A区匹配VIP看台A区价格过滤剔除price max_price的座位库存过滤仅保留stock min_stock的座位并按stock降序排列优先选余票多的。# core/seat_selector.py 片段座位筛选主逻辑 def select_seats(self, raw_seats: List[Dict], config: Dict) - List[Dict]: raw_seats 示例 [ { areaId: 1001, areaName: 内场, price: 880, stock: 2, seatDetail: 1排1座,1排2座 } ] filtered [] for seat in raw_seats: # 区域匹配模糊 if not any(area in seat[areaName] for area in config[target_areas]): continue # 价格过滤 if seat[price] config[max_price]: continue # 库存过滤 if seat[stock] config[min_stock]: continue filtered.append(seat) # 按余票数降序提高下单成功率 return sorted(filtered, keylambda x: x[stock], reverseTrue)这个设计的好处是筛选逻辑与业务强耦合但与网络层完全解耦。你可以轻松替换为“按座位坐标距离舞台中心最近优先”或“按用户历史偏好推荐”只需重写select_seats方法不影响api_client或order_flow。3.3 下单预检与订单创建两次关键 API 调用的参数深挖真正下单前必须调用两个关键接口预检接口/show/orderPreCheck传入场次 ID、座位 ID、票价等返回canBuy: true/false和verifyToken防重复提交令牌创建订单接口/show/createOrder携带verifyToken、用户收货信息、支付方式等返回订单号orderId。这两个接口的难点在于动态参数构造参数来源说明verifyToken预检接口返回一次性令牌5 分钟内有效必须原样传入创建订单接口buyerInfouser_config.yaml中的buyer_list购票人信息数组需包含name,phone,idCard脱敏处理payMoneyseat[price] * quantity必须与预检返回的actualPrice严格一致否则创建订单失败# core/order_flow.py 片段下单主流程节选 def place_order(self, perform_id: str, seat: Dict, config: Dict): # 步骤1预检 precheck_resp self.api_client.precheck_order( perform_idperform_id, seat_idseat[areaId], priceseat[price], quantity1 ) if not precheck_resp.get(canBuy): raise RuntimeError(f预检失败{precheck_resp.get(msg, 未知错误)}) verify_token precheck_resp[verifyToken] # 步骤2构造买家信息从配置读取已做基础脱敏 buyer_info [ { name: config[buyer_list][0][name], phone: config[buyer_list][0][phone][-4:], # 仅传后4位 idCard: config[buyer_list][0][idCard][-4:] # 仅传后4位 } ] # 步骤3创建订单 order_resp self.api_client.create_order( perform_idperform_id, seat_idseat[areaId], verify_tokenverify_token, buyer_infobuyer_info, pay_moneyseat[price] # 注意必须与预检返回的 actualPrice 一致 ) if order_resp.get(orderId): self.logger.info(f✅ 订单创建成功OrderId: {order_resp[orderId]}) return order_resp[orderId] else: raise RuntimeError(f创建订单失败{order_resp})血泪经验pay_money字段必须与预检返回的actualPrice完全相等包括小数位数。曾有开发者因 Python 浮点数精度问题传入880.0而预检返回880导致创建订单 400 错误。解决方案是在构造pay_money时强制转为整数分int(seat[price] * 100)。4. 常见问题排查5 条真实踩坑记录与解决路径4.1 现象扫码登录后auth.py报错TimeoutException: Message: timeout: Timed out receiving message from renderer原因ChromeDriver 版本与本地 Chrome 浏览器版本不匹配或系统缺少字体库导致页面渲染失败。解决运行chrome --version查看 Chrome 版本前往 ChromeDriver 下载页 下载对应版本Ubuntu/Debian 系统需安装字体sudo apt-get install fonts-wqy-zenheiWindows 用户检查是否禁用了硬件加速Chrome 设置 → 系统 → 关闭“使用硬件加速模式”。4.2 现象余票轮询一直返回stock0但浏览器打开同一场次显示有票原因_m_h5_tk签名过期有效期约 10 分钟或mtgsessionidcookie 失效。解决在api_client.py的_generate_h5_sign方法中打印当前self._m_h5_tk值对比浏览器 Network 面板中真实请求的_m_h5_tk若不一致说明登录态丢失需重新运行auth.py获取新 cookie检查系统时间是否准确签名含时间戳误差超过 30 秒即失效。4.3 现象预检接口返回canBuy: false错误信息为活动未开始原因user_config.yaml中的show_id错误或该演出尚未开放购票大麦网常提前数小时开放页面但库存接口仍关闭。解决手动访问https://www.damai.cn/show/{show_id}.html确认页面是否显示“立即购买”按钮使用浏览器开发者工具抓包复制真实performId场次 ID替换配置中的perform_id字段注意show_id≠perform_id。4.4 现象创建订单时返回{code:400,message:参数错误}无具体字段提示原因buyerInfo中身份证号或手机号格式非法大麦网要求 18 位身份证、11 位手机号或payMoney与预检actualPrice不一致。解决在order_flow.py中create_order调用前打印buyer_info和pay_money值与预检响应中的actualPrice对比使用正则校验身份证号re.match(r^\d{17}[\dXx]$, id_card)手机号校验re.match(r^1[3-9]\d{9}$, phone)。4.5 现象脚本运行中突然大量 429 错误随后被限制访问原因poll_interval设置过小如 0.3 秒或同一 IP 下并发请求过多即使单线程高频请求也会被识别为爬虫。解决立即修改user_config.yaml中poll_interval: 0.85重启脚本如需多开实例务必使用不同网络出口如手机热点 家庭宽带禁止同一局域网内多设备高频请求在retry.py的装饰器中增加status_forcelist(429,)让 429 自动进入重试流程而非直接报错。5. 二次开发实战从“能跑”到“好用”的三个改造技巧5.1 技巧一接入企业微信通知让抢票结果实时触达手机原项目仅控制台输出日志但真实场景中你不可能一直盯着终端。最实用的改造是接入企业微信机器人——它免费、稳定、支持 Markdown 格式且无需服务器。改造步骤在config/user_config.yaml中添加wechat_webhook字段已在 2.3 节展示在utils/notify.py中新增send_wechat_alert方法项目未自带需自行创建# utils/notify.py import requests import json def send_wechat_alert(webhook_url: str, title: str, content: str): payload { msgtype: markdown, markdown: { content: f## {title}\n {content}\n\n⏰ {time.strftime(%H:%M:%S)} } } try: requests.post(webhook_url, jsonpayload, timeout5) except Exception as e: print(f[WARN] 企业微信通知失败: {e})在core/order_flow.py的place_order方法末尾插入调用# core/order_flow.py 行末追加 if order_resp.get(orderId): self.logger.info(f✅ 订单创建成功OrderId: {order_resp[orderId]}) # 新增通知 if config.get(notify, {}).get(wechat_webhook): send_wechat_alert( config[notify][wechat_webhook], 抢票成功, f演出{config[show_id]}\n场次{perform_id}\n座位{seat[areaName]}\n订单号{order_resp[orderId]} ) return order_resp[orderId]效果下单成功瞬间手机微信收到带标题、时间、订单号的富文本消息再也不用守着电脑。5.2 技巧二支持多场次并发监控告别“守株待兔”原脚本一次只盯一个show_id但热门演出常有多个城市巡演。手动启多个进程太原始应改为单进程多协程监控。改造思路将user_config.yaml改为列表结构支持多个演出配置在ticket_bot.py主循环中为每个配置启动一个独立的OrderFlow实例使用asyncioaiohttp替代requests实现真正的并发 HTTP 请求注意selenium仍需同步故扫码登录保持串行。# config/user_config.yaml新格式 - show_id: 987654321 target_areas: [内场] max_price: 880 - show_id: 112233445 target_areas: [看台A区] max_price: 580关键点aiohttp的 session 必须 per-config 独立创建避免 cookie 混淆。每个OrderFlow实例持有自己的aiohttp.ClientSession并在初始化时注入登录态 cookie。5.3 技巧三可视化余票趋势用图表代替数字直觉判断“余票从 0 变成 1”比“余票1”更有决策价值。可在core/order_flow.py中增加简易 CSV 日志记录每次轮询的perform_id,area_id,stock,timestamp再用matplotlib绘制折线图。落地代码追加到order_flow.py# 在类初始化中添加 self.stock_log [] # 在轮询逻辑中追加 def log_stock(self, perform_id: str, area_id: str, stock: int): self.stock_log.append({ time: time.time(), perform_id: perform_id, area_id: area_id, stock: stock }) # 每100条写入CSV if len(self.stock_log) 100: self._dump_to_csv() def _dump_to_csv(self): df pd.DataFrame(self.stock_log) df[datetime] pd.to_datetime(df[time], units) df.to_csv(fstock_log_{int(time.time())}.csv, indexFalse) self.stock_log.clear() # 清空内存运行结束后用 Excel 或 Python 脚本读取 CSV绘制datetimevsstock折线图。你会直观看到某场次余票在开售前 30 秒突然从 0 跳到 5这就是系统释放“隐藏库存”的信号——下次可把poll_interval在该时段动态缩短。从那以后我每次部署抢票脚本都强制走一遍python ticket_bot.py --dry-run干运行模式只做登录和配置校验再检查config/user_config.yaml是否有语法错误、show_id是否可访问、target_areas是否匹配真实数据。这三步花不了 2 分钟却能避开 80% 的“启动即失败”。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。