尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python抢票脚本实战:从登录态维持到库存轮询的自动化购票技术拆解

Python抢票脚本实战:从登录态维持到库存轮询的自动化购票技术拆解 1. 抢票脚本的真实技术边界与需求拆解1.1 为什么“毫秒级”是个被过度包装的概念先把话说在前头任何声称能稳定做到“毫秒级自动购票”的脚本都需要打一个问号。我从几年前开始接触各类票务平台的自动化工具踩过的坑比写过的代码还多。所谓毫秒级通常指的是脚本内部发起请求到收到响应的耗时而不是从你点击运行到票落到你账户里的端到端时间。这中间的链路包括本地网络出口延迟、运营商骨干网跳转、目标服务器接入层排队、应用层风控校验、库存扣减事务、订单生成与支付跳转。你能优化的只有本地这一段剩下的全在别人手里。那为什么还有这么多人执着于“毫秒级”因为在大麦网这类高并发场景下热门演出的票往往在开售后的1到3秒内就被瓜分完毕。如果你的请求比其他人晚了几百毫秒库存可能已经归零。所以脚本的核心价值不在于“毫秒”这个数字本身而在于把人工操作的几秒钟压缩到几百毫秒以内让你在起跑线上不落后太多。我实测下来的经验是一个写得不错的Python脚本从检测到开售到提交订单稳定在300到800毫秒之间是现实的。再往下压边际收益极低而且容易触发平台的风控机制。所以这篇内容的目标很明确——帮你搭建一个可靠、可维护、符合平台规则边界的自动购票辅助工具而不是教你去做不可能实现的“绝对毫秒级”。1.2 谁适合看这份实操记录这份内容适合三类人第一类是有一定Python基础想通过一个真实项目把requests、selenium、异步编程这些知识点串起来的开发者第二类是对票务平台前端交互逻辑感兴趣想了解登录态维持、请求签名、库存轮询这些机制的技术爱好者第三类是纯粹想学习自动化测试思路把抢票场景当作一个高并发请求练习场的同学。如果你完全没写过Python我建议先花两天时间把基础语法过一遍至少搞清楚变量、循环、函数、类、异常处理这几个概念。不要求你写得多优雅但至少能看懂别人写的代码逻辑。另外这份内容不涉及任何绕过平台安全机制的手段所有操作都基于平台公开的Web接口和正常的用户行为模拟。1.3 大麦网购票流程的技术拆解要写脚本先得把人工购票的每一步拆成技术动作。我按实际页面交互顺序列一下登录态获取用户扫码或输入账号密码后服务器返回一个包含身份凭证的Cookie。这个Cookie是后续所有请求的通行证。演出详情页加载前端向后端请求演出信息包括场次、票价档位、库存状态。注意这里的库存状态往往是缓存数据不一定实时。选座或选票档用户点击具体票档前端发送一个“锁定库存”的请求。这一步是关键谁先锁到谁就有机会付款。提交订单锁定成功后前端带着锁定令牌去创建订单生成订单号。支付跳转订单创建后跳转到支付页面用户在限定时间内完成付款。脚本要做的就是把这五步中的前三步自动化第四步视情况决定是否自动提交第五步通常建议手动完成因为涉及资金安全。下面我会逐层展开每个环节的实现细节和避坑要点。2. 环境搭建与核心工具选型2.1 Python版本与依赖库的取舍我目前用的是Python 3.10.x这个版本在异步支持和类型提示方面比较成熟第三方库兼容性也好。不建议用3.12以上的最新版有些老库还没跟上容易在安装依赖时卡住。安装过程就不赘述了官网下载安装包勾选“Add Python to PATH”一路下一步就行。装完后在命令行敲python --version确认版本。核心依赖库我选了这几个库名用途选型理由requests发送HTTP请求同步请求够用API简洁社区资料多httpx异步HTTP请求支持HTTP/2异步性能好适合高并发轮询selenium浏览器自动化处理复杂登录和动态渲染页面playwright新一代浏览器自动化比selenium更快API更现代但学习成本略高loguru日志记录比logging更易用输出格式清晰pycryptodome加密签名部分平台接口需要参数签名我最终用的是requests加selenium的组合。requests负责纯接口调用selenium负责登录和需要浏览器环境验证的环节。为什么不全用selenium因为浏览器启动和页面渲染的开销太大一个操作动辄几百毫秒完全达不到“快”的要求。为什么不全用requests因为登录环节涉及滑块验证和动态令牌纯接口模拟难度极高用浏览器过登录再提取Cookie是更稳妥的方案。2.2 浏览器驱动的配置细节selenium需要配合浏览器驱动使用。我用的是Chrome加chromedriver版本必须严格对应。你可以在Chrome地址栏输入chrome://version/查看版本号然后去chromedriver官网下载对应版本。下载后把可执行文件放到Python安装目录的Scripts文件夹下或者手动指定路径。这里有个坑我踩过chromedriver的版本更新往往滞后于Chrome自动更新。某天早上Chrome悄悄升了个小版本你的脚本就报“session not created”错误。解决办法有两个一是关闭Chrome自动更新二是用webdriver-manager这个库自动管理驱动版本。我后来选了后者省心很多。from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager service Service(ChromeDriverManager().install()) options webdriver.ChromeOptions() options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) driver webdriver.Chrome(serviceservice, optionsoptions)上面这段代码里--disable-blink-featuresAutomationControlled和excludeSwitches是为了降低被识别为自动化工具的概率。这不是为了做坏事而是因为很多平台会对selenium的默认特征做拦截导致正常登录都走不通。2.3 项目目录结构设计一个能长期维护的脚本目录结构不能太随意。我习惯这样组织damai_ticket/ ├── config/ │ └── settings.py # 配置文件存放URL、超时时间、重试次数等 ├── core/ │ ├── login.py # 登录模块 │ ├── ticket.py # 票务信息获取与解析 │ ├── order.py # 订单提交逻辑 │ └── utils.py # 通用工具函数 ├── logs/ # 日志输出目录 ├── main.py # 入口文件 └── requirements.txt # 依赖清单这样分的好处是登录失效了只改login.py接口变了只改ticket.py不会牵一发动全身。而且调试的时候可以单独跑某个模块不用每次都从头执行。3. 登录态维持与请求签名机制3.1 扫码登录的自动化处理大麦网的登录方式主要有两种账号密码登录和扫码登录。账号密码登录往往会触发滑块验证自动化处理滑块的成本很高而且有违平台规则。我选择的是扫码登录——用selenium打开登录页面截取二维码区域保存到本地然后手动扫码。扫码成功后脚本自动提取Cookie并保存到本地文件。具体操作步骤用selenium打开大麦网登录页。定位二维码图片元素用element.screenshot()方法截取二维码。把截图保存到本地用系统默认图片查看器打开。用户用手机App扫码确认。脚本轮询检测登录状态一旦跳转成功立即提取driver.get_cookies()。把Cookie列表序列化成JSON写入本地文件。import json import time from selenium.webdriver.common.by import By def save_cookies(driver, pathcookies.json): cookies driver.get_cookies() with open(path, w) as f: json.dump(cookies, f) return cookies def load_cookies(driver, pathcookies.json): with open(path, r) as f: cookies json.load(f) for cookie in cookies: driver.add_cookie(cookie)这里有个细节Cookie是有有效期的通常几小时到几天不等。我建议每次运行脚本前先检查Cookie是否还有效无效就重新扫码。检查方法很简单用保存的Cookie发一个获取用户信息的请求看返回状态码是不是200。3.2 请求头与参数签名的处理大麦网的接口请求不是裸奔的它有一套参数签名机制。简单说前端在发送请求前会把所有参数按一定规则拼接加上一个时间戳和随机数再用某个密钥做一次哈希运算生成一个签名值附在请求里。服务器收到后做同样的运算比对签名是否一致。这套机制的目的是防止请求被篡改和重放。对于脚本来说你需要逆向出签名算法。这个过程涉及对前端JavaScript代码的分析属于比较进阶的内容。我在这里不展开具体的逆向步骤但可以告诉你思路用浏览器开发者工具的Sources面板找到打包后的JS文件搜索关键词如“sign”、“token”、“timestamp”定位到签名函数然后把它翻译成Python代码。需要强调的是逆向签名算法可能违反平台的服务条款。我个人的做法是只使用平台公开的、不需要签名的接口或者通过selenium模拟真实用户操作来触发请求。这样虽然速度慢一些但合规性更好也不容易因为算法变更导致脚本失效。3.3 会话保持与异常重连网络请求最怕的就是会话中断。我遇到过好几次脚本跑了半小时突然所有请求都返回401一看是Cookie过期了。为了解决这个问题我在代码里加了一个会话检查机制import requests from loguru import logger class SessionManager: def __init__(self, cookies): self.session requests.Session() self.session.cookies.update(cookies) self.session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..., Referer: https://www.damai.cn/ }) def check_alive(self): try: resp self.session.get(https://www.damai.cn/, timeout5) return resp.status_code 200 except Exception as e: logger.error(f会话检查失败: {e}) return False def request_with_retry(self, method, url, max_retries3, **kwargs): for i in range(max_retries): try: resp self.session.request(method, url, timeout5, **kwargs) if resp.status_code 200: return resp logger.warning(f请求返回{resp.status_code}第{i1}次重试) except requests.RequestException as e: logger.error(f请求异常: {e}第{i1}次重试) time.sleep(0.5) return None这个SessionManager类封装了会话检查和自动重试。check_alive方法用来判断当前会话是否还有效request_with_retry方法在请求失败时自动重试最多三次。重试间隔设为0.5秒太短了没用太长了错过抢票时机。4. 库存监控与订单提交的实操逻辑4.1 库存轮询的频率控制策略库存监控的本质是不断向服务器询问“还有票吗”。但询问频率是个需要仔细权衡的参数。问得太慢票没了你都不知道问得太快服务器直接把你限流甚至封IP。我实测下来的经验值开售前5分钟开始轮询间隔设为1到2秒开售瞬间把间隔压缩到200到500毫秒开售后30秒如果还没抢到把间隔放宽到3到5秒避免被风控盯上。为什么开售瞬间要压缩间隔因为库存释放的那一瞬间所有请求都在抢同一个资源。你的请求早到100毫秒成功率就高一大截。但为什么不能一直保持200毫秒因为服务器有频率限制连续高频请求超过一定次数会触发验证码或者直接拒绝服务。import time from datetime import datetime def poll_ticket(session, url, start_time, fast_interval0.3, slow_interval2.0): while True: now datetime.now() if now start_time: time.sleep(1) continue elapsed (now - start_time).total_seconds() interval fast_interval if elapsed 30 else slow_interval resp session.request_with_retry(GET, url) if resp and check_stock(resp.json()): return resp.json() time.sleep(interval)这段代码的逻辑是开售前每秒检查一次时间开售后30秒内用0.3秒间隔轮询30秒后用2秒间隔。check_stock函数负责解析返回的JSON判断目标票档是否有库存。4.2 订单提交的参数构造与时机把握检测到库存后下一步是提交订单。这一步的请求参数通常包括演出ID、场次ID、票档ID、购买数量、用户ID、以及一个从库存锁定接口拿到的令牌。这些参数大部分可以从之前的接口响应中提取令牌则需要从锁定接口的返回值里拿。订单提交的时机很关键。太早了库存还没释放请求会被拒绝太晚了票被别人抢走。我的做法是一旦check_stock返回True立即用拿到的令牌构造订单请求不做任何多余的日志输出或界面更新直接发出去。def submit_order(session, order_url, params): headers { Content-Type: application/json, X-Requested-With: XMLHttpRequest } resp session.request_with_retry(POST, order_url, jsonparams, headersheaders) if resp and resp.status_code 200: result resp.json() if result.get(success): return result.get(orderId) return None这里有个坑订单提交接口往往有防重复提交机制。如果你在短时间内连续提交同一个令牌服务器会拒绝后面的请求。所以拿到令牌后只能提交一次失败了就得重新去锁定库存。我在代码里加了一个标志位确保每个令牌只用一次。4.3 支付环节的自动化边界订单创建成功后会跳转到支付页面。我的建议是支付环节手动完成。原因有三第一自动支付涉及资金安全万一脚本出bug重复支付损失的是真金白银第二支付页面通常有额外的安全验证自动化处理成本高第三订单创建成功后通常有5到15分钟的支付窗口足够你手动操作。脚本在创建订单成功后可以播放一个提示音或者发送一个桌面通知提醒你赶紧去付款。我用的是plyer库发系统通知简单几行代码就能搞定from plyer import notification notification.notify( title抢票成功, message订单已创建请尽快完成支付, timeout10 )5. 常见问题排查与避坑经验实录5.1 登录态失效的典型表现与修复登录态失效最明显的表现就是所有请求都返回401或者跳转到登录页。我遇到过几种不同的失效场景第一种是Cookie自然过期。大麦网的登录Cookie有效期大概是24小时超过时间就需要重新扫码。解决办法是在脚本启动时先检查Cookie文件的时间戳超过20小时就提示重新登录。第二种是异地登录导致踢下线。如果你在手机上也登录了同一个账号网页端的会话可能会被强制失效。这种情况只能重新扫码。第三种是平台主动清理异常会话。如果你的请求频率过高或者行为模式异常平台可能会提前使你的会话失效。这时候除了重新登录还要反思一下是不是轮询间隔设得太激进了。5.2 请求被限流或返回验证码的应对限流的表现是返回429状态码或者响应体里出现“请求过于频繁”的提示。验证码则是在请求响应中返回一个图片验证码的URL要求你输入正确答案。我踩过的坑有一次把轮询间隔设成了100毫秒跑了不到两分钟就被限流了而且限流持续了将近半小时才恢复。从那以后我再也不敢把间隔设得太低。应对限流的策略立即停止所有请求等待至少5分钟再试。检查代码里是否有不必要的重复请求比如同时轮询多个接口。在请求头里加上Cache-Control: no-cache避免拿到缓存的旧数据。如果频繁触发验证码考虑降低频率或者改用selenium模拟人工操作。5.3 库存显示有票但提交失败的排查这种情况最让人抓狂明明看到库存显示有票一点提交就提示“库存不足”或“系统繁忙”。原因通常是这样的你看到的库存是缓存数据实际库存已经被其他请求锁定了。或者你的请求到达服务器时库存刚好被扣完。排查思路检查库存接口的响应头看是否有Cache-Control字段如果有说明数据可能来自缓存。对比库存接口和订单提交接口的时间差如果超过500毫秒失败概率会显著增加。尝试在库存接口返回有票后立即调用锁定接口而不是直接提交订单。锁定成功后再提交成功率更高。下面这张表整理了我遇到过的典型问题和对策问题现象可能原因解决思路401 UnauthorizedCookie过期或会话被清理重新扫码登录更新Cookie文件429 Too Many Requests请求频率过高触发限流停止请求5分钟降低轮询频率库存有票但提交失败缓存数据滞后或库存被抢先调锁定接口再提交订单滑块验证码出现行为被判定为自动化改用selenium模拟人工操作订单重复提交被拒同一令牌多次使用每个令牌只提交一次失败后重新锁定脚本运行中突然卡死网络超时或浏览器无响应加超时参数设置最大重试次数5.4 脚本运行环境与网络优化建议脚本跑在什么环境里对成功率有直接影响。我试过在本地电脑、云服务器、以及朋友家的宽带上跑同一个脚本结果差异很明显。本地电脑的优势是网络环境稳定劣势是晚上抢票时家里其他设备在占用带宽。云服务器的优势是网络延迟低、带宽充足劣势是需要额外配置环境而且部分平台会对数据中心IP做限制。我个人的选择是用本地电脑但抢票时关闭其他占用带宽的应用最好用网线而不是Wi-Fi。另外DNS解析速度也会影响请求延迟。我把DNS换成了公共DNS解析大麦网域名的速度从平均80毫秒降到了20毫秒左右。这个优化虽然不大但在抢票场景下每一毫秒都值得争取。还有一个容易被忽略的点系统时间同步。如果你的电脑时间比标准时间慢了2秒那你的脚本就会晚2秒开始轮询基本等于白给。我建议开启系统自动时间同步或者在脚本启动时用NTP协议校准一次时间。6. 代码组织与可维护性实践6.1 配置与代码分离的原则把配置写死在代码里是新手常犯的错误。演出ID变了要改代码票档变了要改代码连超时时间调整都要改代码。正确的做法是把所有可变参数抽到一个配置文件里。# config/settings.py CONFIG { performance_id: 123456789, session_id: 987654321, price_id: 555555, quantity: 1, start_time: 2025-06-01 20:00:00, fast_interval: 0.3, slow_interval: 2.0, max_retries: 3, cookie_path: cookies.json, log_path: logs/ }这样改配置不用动核心逻辑也方便用不同的配置文件跑不同的演出。6.2 日志记录与运行状态监控日志是排查问题的生命线。我用loguru替代了标准库的logging原因是它的输出格式更友好支持彩色终端输出而且写文件很方便。from loguru import logger logger.add(logs/ticket_{time}.log, rotation1 day, retention7 days) logger.info(脚本启动) logger.debug(f当前轮询间隔: {interval}秒) logger.success(f订单创建成功订单号: {order_id}) logger.error(f请求失败: {resp.status_code})日志级别我建议用DEBUG起步正式跑的时候调到INFO。DEBUG级别会输出每次请求的URL和响应时间方便分析性能瓶颈。但日志量会很大记得设置文件轮转和保留天数。6.3 异常捕获与优雅退出脚本最怕的就是抛异常直接崩掉。尤其是在抢票的关键时刻一个未捕获的异常可能导致整个流程中断。我的做法是在主循环外面包一层try-except捕获所有异常并记录然后根据异常类型决定是重试还是退出。def main(): try: session login_and_get_session() ticket_info poll_ticket(session, ...) if ticket_info: order_id submit_order(session, ...) if order_id: notify_user(order_id) except KeyboardInterrupt: logger.info(用户手动终止) except Exception as e: logger.exception(f未预期异常: {e}) finally: cleanup()KeyboardInterrupt用来处理用户按CtrlC的情况finally块里做资源清理比如关闭浏览器驱动、保存日志等。7. 合规使用与风险提示7.1 平台规则的红线在哪里写这类脚本之前必须搞清楚哪些事能做哪些事不能做。我的原则很简单模拟正常用户操作不破坏平台服务不牟利。具体来说不使用多账号批量抢票这属于黄牛行为。不绕过平台的验证码和风控机制遇到验证码就手动处理。不将脚本用于商业用途比如帮别人代抢收费。不频繁请求导致服务器压力过大轮询间隔不低于200毫秒。平台的服务条款通常会禁止自动化工具的使用。虽然个人学习研究和小范围使用一般不会被追究但大规模滥用肯定会导致账号被封。我见过有人用脚本一天抢了几十张票然后倒卖结果账号被永久封禁得不偿失。7.2 技术学习的正确姿势把抢票脚本当作一个学习项目而不是一个牟利工具心态会好很多。通过这个项目你可以学到HTTP协议的实际应用包括请求头、Cookie、状态码。浏览器自动化的基本操作selenium或playwright的API使用。异步编程和并发控制如何在高频请求中保持稳定。日志记录和异常处理如何让程序在出错时优雅降级。配置管理和代码组织如何写出可维护的脚本。这些技能在爬虫、自动化测试、运维监控等领域都是通用的。与其纠结于“毫秒级”这个噱头不如把精力放在理解底层原理上。7.3 替代方案与手动抢票技巧如果你不想写代码或者觉得脚本风险太高也有一些手动抢票的技巧可以分享提前登录并保持页面活跃避免开售时还要走登录流程。使用大麦网的App而不是网页App的响应速度通常更快。开售前30秒开始疯狂点击不要等页面自动刷新。如果第一波没抢到不要放弃开售后5到15分钟往往会有未付款的票回流。关注演出主办方的官方账号有时候会放出额外的票源。我个人的体会是脚本能帮你提高效率但不能保证100%成功。热门演出的票本身就是稀缺资源供需关系决定了大部分人抢不到是常态。保持平常心抢到了是运气抢不到也别太在意。
返回列表