
简介面向Python学习者及有抢票需求的用户这是一份自动化购票脚本源码包以Python实现模拟登录、监控票务并自动提交订单内置选座购买逻辑适合参考网络请求、HTML解析与定时任务等实践。压缩包共10个文件包含2个Python主程序与工具脚本、2张流程图/示例截图、1份Markdown说明文档、1个JS辅助文件及依赖清单整体仅1.37MB结构清晰便于快速上手。已有10806人学习下载。源码包提供完整的抢票实现代码与配置入口涵盖登录参数、票价选择、座位筛选等模块已实现选座购买、暂不支持连坐同时附带流程图和界面示例图帮助理解自动购票流程README给出安装与运行指南可直接在Python环境中部署试运行。对想掌握自动化脚本、应对验证码等反爬机制的开发者是很好的参考项目。1. 大麦网抢票脚本 Python 源码不是外挂是一套时序竞争工程先说结论大麦网抢票脚本 Python 源码本质上不是某个神秘的“秒杀神器”而是一套把账号登录、场次选择、座位勾选、订单提交串成流水线的自动化程序。它要解决的核心问题只有一个——在库存释放的瞬间把请求尽可能早地送到服务器同时绕过登录验证、风控校验和接口特征识别这几道关卡。适合的读者是有 Python 基础、愿意自己维护脚本逻辑而不是买成品、想搞清楚每一步在干什么的开发者。需要泼一盆冷水脚本并不保证百分百抢到它能做的是把“看到有票再手动点”变成“余票出现后自动提交”把点击延迟从人类的几百毫秒压缩到程序的上百毫秒。成功率取决于账号权重、网络延迟、并发窗口和验证码处理四者缺一不可。本文不会给你一份注释翔实到可以直接粘贴运行的完整源码因为大麦网的接口参数和风控策略经常调整任何固定版本都会过期。我会按最常用的技术路线把整个脚本的骨架、关键参数、常见翻车点和调优方向拆开讲你照着搭再根据当天接口变化微调才是一条走得通的路。2. 从登录到下单抢票脚本的技术链路与三大核心模块2.1 会话保持与风控线索requests 还是 playwright写抢票脚本的第一步是选技术底座。常见做法是两条路线纯 requests 方式和浏览器自动化方式。纯 requests 方式直接模拟 HTTP 请求优点是轻量、并发高、内存占用小一个进程可以同时跑几十个会话缺点是必须自己处理登录态、加密参数、请求头风控字段一旦接口加了新的签名逻辑脚本就立刻失效。浏览器自动化方式用 playwright 这类工具驱动真实浏览器登录验证码、滑块拼图这类反爬手段基本能自动过缺点是每个浏览器实例要占几百兆内存并发起不来而且窗口多了容易触发设备指纹风控。我的经验是脚本主体用 requests验证码环节单独用 playwright 处理。登录时先用 playwright 打开浏览器、完成扫码或滑块验证、拿到带 cookie 的会话然后把 cookie 导出给 requests 复用。这样既绕过了登录环节最难的验证码又保住了后续高频请求的低延迟优势。如果你只抢热门演出、不需要多账号并发全流程用 playwright 也没问题但要把超时和重试参数调好。2.2 场次与票档的选择策略接口轮询与库存判定登录态拿到之后脚本的核心工作就是盯着目标场次的库存状态。大麦网 App 和网页版有各自独立的接口网页版的接口相对稳定但部分演出只开放 App 端抢票这时抓包工具就派上用场了。一般做法是先用抓包工具记录一次手动选择场次、票档、数量的完整流程找到三个关键接口场次列表接口、余票查询接口、下单接口。场次列表通常在页面加载时请求一次就够了余票查询才是轮询的重点。轮询的时候不要每个循环都完整走一遍 HTTP 请求而是要在循环外复用 requests.Session 对象让连接复用 TCP 通道减少握手开销。库存判定这块有个容易踩的误区不要只盯着“是否有票”这个布尔值要看返回数据里的库存状态码。很多场次在开售前会返回“未开售”开售瞬间变成“可售”再往后变成“无票”中间还有一个“余票紧张”的状态。脚本的轮询条件要写成只有当状态码处于可售区间才触发下单否则继续循环。2.3 订单提交时序从余票探测到付款页的关键几秒余票探测和下单之间是整个脚本里最要命的时间窗口。库存释放后大量用户同时提交订单服务器会在几百毫秒内把库存锁给最快的一批请求。轮询到有票再发起下单请求这中间多出的一次网络往返可能就是成败的分水岭。所以成熟一点的方案会把“探测”和“下单”拆成两个并行动作一个协程在疯狂轮询余票另一个协程在探测到可售状态的瞬间立即执行下单接口。下单接口的参数要在开售前就提前构造好包括场次 ID、票档 ID、购买数量、收货人信息不能等到拿到余票结果再去查一遍名单。订单提交成功后脚本一般会停在订单确认页或者直接引导到付款页。大麦的支付流程有独立的订单号生成逻辑而且部分场次要求下单后几分钟内付款否则自动释放。脚本要在提交订单后立刻轮询订单状态拿到待支付状态就调用本地通知或者自动打开浏览器让用户手动付款这一步不建议全自动付款环节涉及资金安全留给人来确认更稳妥。3. 源码骨架一个最小可用抢票脚本的分层实现3.1 会话管理模块cookie 的导入与复用前面说了验证码用 playwright 过后续用 requests 跑。这块的代码就是把浏览器 cookie 导出来转成 requests 能用的格式。一个最小实现大概是下面这样。import asyncio import json from playwright.async_api import async_playwright async def get_cookie_from_browser(): # 用 playwright 打开浏览器人工扫码或过滑块 async with async_playwright() as p: browser await p.chromium.launch(headlessFalse) context await browser.new_context() page await context.new_page() await page.goto(https://example.com/login) # 等待用户手动完成登录超时设为 120 秒 await page.wait_for_url(**/home, timeout120000) cookies await context.cookies() with open(cookies.json, w, encodingutf-8) as f: json.dump(cookies, f, ensure_asciiFalse, indent2) await browser.close() if __name__ __main__: asyncio.run(get_cookie_from_browser())这段代码的核心是page.wait_for_url它会阻塞直到页面跳转到登录后的首页。实际地址要按当天的页面跳转逻辑改有些演出登录后留在原页面那就改成等某个元素出现。cookie 存成 JSON 后requests 的会话里直接循环 set 进去就行。cookie 有两个要注意的地方一是大麦的 cookie 里有设备指纹相关的字段不能手动删掉删了会被判定为新设备重新验二是过期时间要看具体字段有的几小时就失效有的能撑几天脚本开场前记得验证一次 cookie 有效性。3.2 余票轮询模块毫秒级探测的最小循环轮询模块要控制节奏不能像 while True 一样死循环得带一个可以调节的间隔参数。下面这段是我常用的骨架。import time import requests def poll_ticket_availability(session, target_sku_id, interval0.1): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://example.com/detail, Accept: application/json, } while True: try: resp session.get( https://example.com/api/stock/query, params{skuId: target_sku_id}, headersheaders, timeout2, ) data resp.json() stock_status data.get(data, {}).get(stockStatus) # 状态码 1 表示可售0 表示未开售-1 表示无票 if stock_status 1: print(可售状态触发下单) return True except requests.exceptions.RequestException as e: print(f轮询请求失败: {e}) time.sleep(interval)轮询间隔是个关键参数设太短会打爆接口导致 IP 被限设太长又抢不到热门的票。常见做法是先按 200 毫秒跑如果连续几分钟没有一次请求报错再把间隔降到 100 毫秒甚至 50 毫秒。这里的timeout2也很重要接口假死的时候不会把整个脚本拖死。另外要注意轮询接口的返回格式不是固定的。有的场次返回 JSON有的返回一个带加密字符串的字段需要二次解密。遇到这种情况先抓包看一次返回数据长什么样再决定怎么解析。别假设所有接口都是明文。3.3 下单与验时模块订单提交和防超卖重试下单模块要处理的细节最多。参数签名、场次 ID 拼接、订单号回执验证每一环都可能翻车。核心结构大概是下面这样。def submit_order(session, item_id, sku_id, quantity1, buyer_infoNone): payload { itemId: item_id, skuId: sku_id, quantity: quantity, buyer: buyer_info or {}, # 部分接口要求带上一个由服务器下发的 token token: session.cookies.get(order_token, ), } headers { Content-Type: application/json;charsetUTF-8, Origin: https://example.com, } resp session.post( https://example.com/api/order/create, jsonpayload, headersheaders, timeout3, ) result resp.json() if result.get(success): order_id result[data][orderId] print(f订单创建成功: {order_id}) return order_id else: # 常见失败码: 2001 库存不足, 2002 风控拒绝, 2003 参数错误 print(f下单失败: {result.get(code)} - {result.get(message)}) return None这里的order_token不是常态存在的很多场次由接口在某个前置步骤下发需要在上一步的响应里取到并存在 session 里。下单失败后的重试要讲究策略库存不足就别重试了重试也是徒劳风控拒绝必须停下来换登录态或换 IP参数错误要回头检查上一步的数据是否过期。延迟抖动也放在这个模块里做。每次下单请求前加一个random.uniform(0.2, 0.8)的随机停顿避免多个请求的到达时间完全一致这种“齐步走”的请求特征最容易触发风控。4. 忘掉“秒杀”幻觉抢票脚本的 5 个常见翻车点与排查思路4.1 接口 403缺了风控头信息现象是脚本启动后轮询请求大面积返回 403浏览器里手动访问同一接口却正常。原因是服务器的反爬网关在检查请求头里的User-Agent、Referer、Accept-Language甚至是Sec-Fetch-Site这类浏览器自动带的字段。纯 requests 默认的请求头太干净一眼就能认出是脚本。解决方法是把抓包工具里浏览器发出的完整请求头复制下来原样塞进 requests 的 headers 里。之后每次请求都会带上这些字段403 的问题基本能解决。注意部分接口会对Referer做校验跳转来源对不上也会拒绝。4.2 显示有票却提交失败库存二次校验现象是轮询结果明明返回了可售状态但下单接口总是返回“库存不足”或“手慢了”。原因是余票查询接口和下单接口之间隔了几百毫秒而在这段时间里库存已经被别人的请求锁定了。查询接口的数据是近似的下单接口才是最终裁决。解决方法是把轮询间隔进一步压缩同时在下单接口的 payload 里带上查询接口返回的库存版本号。有些后端支持“版本号校验”如果版本号对不上就代表库存已经变化这时候要么重新查库存要么放弃本次提交。盲目的连续重试只会加重风控标记。4.3 验证码反复出现设备指纹被标记现象是同一账号在浏览器里登录只需一次验证脚本里用 playwright 过完验证码后下一次登录又弹验证码。原因是服务器记录了浏览器指纹包括 Canvas 指纹、WebGL 渲染结果、字体列表这些。playwright 每次新开浏览器实例指纹都不同就被当成新设备。解决方法是固定使用同一个浏览器上下文目录playwright 里可以设置launch_persistent_context把浏览器数据目录固定下来这样指纹就稳定了。另外也可以考虑在同一个上下文里完成登录和下单全流程不要登录和抢票拆成两次浏览器会话。4.4 脚本能跑但抢不到连接没有预热现象是脚本明明从开售前就开始轮询但开售瞬间下单请求的耗时比其他人的请求多出一大截。原因是 TCP 连接和 TLS 握手是在开售那一刻才发起的多出来的这几百毫秒在全网竞争里非常致命。解决方法是提前建立连接。脚本在开售前 10 分钟就开始请求商品详情页用requests.Session把连接池填充好。部分实现还会提前调用一次余票查询接口后续轮询直接复用同一条连接省去每次请求的握手开销。4.5 账号被限制购单日下单频率触发风控现象是脚本在某天抢到了票第二天再用同一账号就提示“操作频繁”或直接要求人脸验证。原因是大麦网的后台会统计每个账号的当日订单提交次数、接口调用频率和登录地变化。脚本高频轮询加上多次下单尝试很容易触发限制。解决方法是控制每个账号每天的脚本运行次数同一账号不要反复上下线也不要一天内抢多场演出。如果只是帮自己和朋友买用脚本跑同一个账号风险最大不如把账号分散开每场用不同的账号执行。5. 参数调优与边界控制把成功率从“碰运气”拉到“高并发”5.1 并发数量与线程模型怎么选很多人一上来就开 50 个线程指望靠量取胜结果风控瞬间启动账号全被限制。常见做法是单账号不要开超过 3 个并发轮询协程。同一账号在同一场次的请求频率必须保持在每秒 20 次以内超过这个阈值就会触发风控。多账号的并发可以放宽但每个账号的 IP 要尽量固定切换 IP 的动作会打断登录态反而得不偿失。线程模型上我建议用asyncio而不是传统 threading。协程的切换成本低单个进程可以挂几百个轮询任务而且代码里天然适合做超时控制。如果你的网络环境有丢包可以开一个线程专门清理超时的连接避免协程堆积。5.2 三个影响成败的延迟参数第一个是轮询间隔。开售前用 300 毫秒跑开售瞬间切到 50 到 100 毫秒但必须在代码里设置一个最短间隔的常量防止网络波动时循环失控。第二个是请求超时。查询接口的超时建议设为 2 秒下单接口设为 3 秒。超时设太长脚本会卡在等待响应上错过后续的请求窗口设太短正常的网络抖动会被误判为失败。第三个是重试间隔。下单失败后的重试不能立即执行我一般用random.uniform(0.5, 1.5)做随机退避。重试超过三次就直接放弃把机会留给下一次轮询的触发。5.3 接口参数中容易被忽略的字段有些接口的参数里藏着动态 token、设备 ID 和匿名 ID这些字段直接决定了请求能否通过后端校验。最容易被忽略的是deviceId。纯 requests 方案没有浏览器环境这个字段一般是通过某个 JS 函数生成的直接硬编码进请求头会有过期问题。你可以用 playwright 打开一次页面在页面上下文里执行对应的 JS 代码生成 deviceId抓出来作为常量半天内基本有效。另一个是sign签名。部分接口要求在参数拼接后做 MD5 或 SHA256 摘要密钥从某个 JS 文件里加盐生成。这个密钥每次版本更新都变没有固定解法只能靠抓包分析。5.4 动态接口返回与降级预案目前大麦网的反爬策略在往“动态接口返回”方向走也就是说你抓包看到的接口地址可能隔几天就换一次返回字段的命名也会变化。脚本写好后不能放着不管开场前花十分钟快速验证一遍接口是否正常是个非常值的习惯。如果 requests 方案某天完全跑不通降级预案是切回 playwright 全流程。虽然慢但接口变化对浏览器自动化方案的影响小得多。我一般会在脚本里做一层抽象requests 方案作为默认路径一旦连续失败超过五次自动拉起 playwright 兜底。代码层面不需要做太复杂两个类实现同一个下单方法就够了。6. 从脚本到稳定赢面一个值得养成的验收习惯脚本写完之后不建议直接拿真账号真演出去试那样既浪费机会又容易触发风控。更稳妥的做法是先搭一个本地 mock 服务把大麦网的几个关键接口按你抓包到的格式复现出来然后让脚本去请求这个 mock 服务验证全流程时序是否正确。mock 可以用 Flask 起一个极简服务接口路径和返回内容和真实环境保持一致。开售状态用一个后台线程去切换比如设定 10 秒后从“未开售”变成“可售”看脚本能不能在状态切换后的第一次轮询里触发下单。这个测试跑通了再去真环境里做一次小成本验证。我吃过大亏有一版脚本在 mock 环境里跑得很顺结果上了真环境后下单接口返回了一个 mock 里没写过的错误码。查了半天发现是请求头里少了一个由前置接口动态下发的字段。从那之后我养成了一个习惯——每次开售前先手动用浏览器跑一次完整的下单流程抓包对比一下脚本发出的请求确认没有遗漏再放脚本上线。另外抢票脚本这种代码有一个天然矛盾你希望它越快越好但大麦网的反爬系统也在不断升级。过快的请求、过整齐的频率、对不上号的头信息都会让脚本在三分钟内失效。把参数调优当成持续的过程而不是一次写完就结束。这套方案值不值得投入取决于你抢的演出热门程度和账号数量。如果你的目标场次经常是开售即罄的重磅演出脚本能给你的只是比别人快零点几秒的优势这个优势在强实名制和无条件退款规则面前有时候并不足以保证成功。但如果目标是小众场次、冷门票档脚本的自动化程度会大幅降低你守着页面反复刷新精力消耗这种场景下投入产出比更高。希望这篇实战拆解能帮到你。本文还有配套的精品资源点击获取