Python自动化购票脚本:requests直连接口实现登录监控与下单

发布时间:2026/10/19 8:19:52
Python自动化购票脚本:requests直连接口实现登录监控与下单 简介针对秀动网页端购票流程的自动化工具资源面向有抢票需求但缺乏编程基础或希望提升效率的用户解决手动刷新下单慢、错过余票等问题。压缩包共5个文件涵盖Python脚本、可执行exe程序、JavaScript辅助脚本、HTML使用教程及JSON配置文件大小约30.44MB兼顾开箱即用与二次开发需求。其中exe免去环境配置适合直接运行pyc与js便于查看内部逻辑json可用于调整参数。配套教程对验证流程和常见异常做了说明帮助读者快速跑通自动购票流程。目前已有5636人学习适合对网页自动化购票感兴趣、具备一定动手能力的用户参考实践并可基于脚本来扩展或适配其他活动场次。1. 开票 30 秒后我的 Python 脚本已经把订单提到支付页开票 30 秒后网页端还停在选场次页我的 Python 脚本已经把订单提到支付页。这是我第一次跑通某票务平台网页端自动化购票时的真实体验。这个项目的本质很简单把登录、查场次、看库存、提交订单这一串人工操作换成 Python 在秒级完成。它不依赖浏览器渲染也不用 Selenium 去模拟点击而是直接把网页端背后的 JSON 接口按顺序调通。适合两类人一是自己追演出但手速不够快的普通用户二是想练网页端自动化、接口模拟、并发控制的开发新手。需要先说明这个项目定位是个人自用和学习。你拿它给自己抢一张票没问题但不要用来批量囤票或恶意刷票更别想去对抗平台的风控规则。脚本只做“按时查库存、按规则下单”至于付款、选座、实名核验这些步骤仍然需要人工确认。2. 整体架构与技术选型四层模块组成的抢票数据流自动化购票最容易被带偏的一点是一上来就上浏览器自动化。很多教程会告诉你用 Selenium 或 Playwright 打开页面模拟点击购票按钮看起来直观但到了开票瞬间浏览器加载脚本、渲染 DOM 都需要时间而且页面倒计时组件和你本地时间不一定准脚本很容易跟着一起翻车。真正稳的做法是把网页端当成一组 JSON 接口用 requests 直接请求拿到结构化数据后自己判断库存和提交订单。2.1 技术栈选型为什么是 requests 而不是 selenium这个项目采用 Python 3.10 requests threading sqlite3 logging属于很典型的轻量级自动化项目。requests 负责所有 HTTP 请求threading 负责同时监控多个场次和票档sqlite3 记录每次下单结果logging 把每个关键节点写成日志方便开票之后复盘。为什么不用 Selenium因为网页端购票接口返回的是 JSON并不需要完整渲染页面。requests 更轻、更快、更容易控制请求频率。另一个原因是浏览器自动化在并发场景下非常耗内存一个 Chromium 实例就要几百 MB而 requests 会话只需要一个常驻进程就能稳定挂在后台。开票瞬间拼的是响应速度不是页面渲染速度。常见做法是先用浏览器开发者工具里的 Network 面板看一遍完整的购票请求顺序。你会发现网页端的每一次点击背后就是几个 POST 请求登录、查活动、查库存、创建订单、确认订单。我们把这些请求包装成 core 和 api 两层api 层只管接口地址和参数模板core 层管登录、监控、下单。将来平台接口有变动大概率只需要改 api 层不用去翻核心业务逻辑。2.2 目录结构与模块职责项目最终被我整理成下面这个结构每个模块职责单一二次开发时好定位问题ticket_bot/ ├── main.py # 入口解析参数读取配置启动监控 ├── requirements.txt # 第三方依赖 ├── config.ini # 账号、场次、开票时间、请求频率 ├── core/ │ ├── __init__.py │ ├── session.py # Session 复用Header 统一管理 │ ├── login.py # 二维码登录 / 短信验证码登录 │ ├── monitor.py # 倒计时、库存轮询 │ └── order.py # 下单、查单、幂等保护 ├── api/ │ ├── __init__.py │ └── endpoints.py # 接口地址与请求体模板 ├── storage/ │ ├── __init__.py │ └── db.py # SQLite 操作 ├── notify/ │ ├── __init__.py │ └── pusher.py # 邮件 / 移动端 App 推送 └── utils/ ├── __init__.py ├── timeutil.py # 平台时间校准 └── retry.py # 带退避的重试装饰器各模块职责先说清楚。core/session.py 管理整个会话生命周期requests.Session 会自动保留 Cookie但有些平台用的是自定义 token 放进 Header所以这里要集中处理。core/login.py 放二维码登录和短信登录登录成功后的 token 只存内存不落盘。core/monitor.py 是抢票主循环负责倒计时和库存判断。core/order.py 负责创建订单并且必须保证同一场次不会重复提交。api/endpoints.py 把所有接口地址聚合在一个字典里方便维护。比如 query 库存、创建订单、活动详情统一定义在这里。storage/db.py 用 SQLite 保存订单notify/pusher.py 在下单成功后推送通知utils/timeutil.py 解决本地时间和平台时间不一致的问题utils/retry.py 给网络请求加退避重试。main.py 读 config.ini根据命令行参数决定进入登录模式还是监控模式。2.3 一次购票请求的完整数据流从 main.py 开始先加载 config.ini然后创建一个统一 Session再调用 timeutil 校准时间。如果配置里开了时间校准脚本会请求一次平台服务器时间计算本地时间与平台时间的偏移量后续倒计时都基于这个偏移量计算。登录完成之后进入 monitor 模式。# main.py 简化逻辑 def main(): cfg load_config(args.config) session build_session(cfg) if args.sync_time: sync_time(session) if args.mode login: login_by_qrcode(session, cfg) return if args.mode monitor: login_by_qrcode(session, cfg) monitor StockMonitor(session, cfg, on_stock) monitor.run()这段代码的逻辑是登录和监控是两个独立模式。第一次可以先跑--mode login把登录态确认好第二次直接跑--mode monitor进入抢票循环。sync_time在开票前执行一次主要目的是把本地时钟和平台时钟对齐。build_session会统一塞入 User-Agent、Referer、Origin 等请求头这一步非常关键不然很容易被当成异常请求。进入 StockMonitor 以后脚本会先根据activity_id拉取活动详情确认配置里的sku_id是否有效。然后计算距离sale_start的秒数开票前保持低频查询比如 2 秒一次开票前 500ms 切换成高频查询比如 200ms 一次。一旦发现目标 sku 的库存大于 0立即触发on_stock回调这个回调会去 core/order.py 里创建订单。api/endpoints.py 中的接口定义大致是这样的API { qrcode_create: (POST, /api/qrcode/create), qrcode_check: (POST, /api/qrcode/check), activity_detail: (GET, /api/activity/detail), stock: (GET, /api/activity/stock), order_create: (POST, /api/order/create), order_check: (POST, /api/order/status), }这里要特别说明接口路径只是示例实际以你自己抓包结果为准。不同平台的路径命名差异很大有的用/api/v1/order有的用/api/trade/create。维护在一个字典里是为了在二次开发时只改一处。订单创建成功后storage/db.py 会写入一条记录。建表语句如下CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, activity_id TEXT NOT NULL, sku_id TEXT NOT NULL, order_id TEXT UNIQUE, status TEXT DEFAULT pending, created_at TEXT DEFAULT (datetime(now, localtime)) );order_id设置唯一约束是为了防止并发线程插入重复记录。后续查单、取消订单、支付跳转都用这张表里的order_id作为依据。下单成功后 notify/pusher.py 会推送一条包含支付链接的消息看起来就是“已出票/待支付”的状态。到这里一次完整的购票数据流就走完了。3. 核心实现拆解登录、监控、下单三个关键函数这一章是落地重点我把项目里最核心的三段代码单独拿出来讲。登录是前提库存监控是速度来源下单是最终结果。每一段代码后面都会说明参数含义和容易出错的地方。3.1 二维码登录与短信登录网页端购票通常不提供明文密码登录常见的是手机验证码和二维码扫码。这个项目优先实现二维码登录因为二维码登录的 token 有效期长而且不需要手动输入验证码。# core/login.py def login_by_qrcode(session, config): resp session.post(API[qrcode_create][1], json{client_id: CLIENT_ID}) data resp.json()[data] qr_key data[qr_key] qr_url data[qr_url] print(f请使用票务平台 App 扫码: {qr_url}) # 状态码: 1 表示未扫码2 表示已扫码未确认3 表示确认登录 for i in range(120): time.sleep(2) r session.post(API[qrcode_check][1], json{qr_key: qr_key}) state r.json()[data][status] if state 3: token r.json()[data][token] session.headers[Authorization] fBearer {token} return True raise RuntimeError(二维码登录超时)这段代码先创建一个二维码然后每 2 秒轮询一次扫码状态。qr_key是服务端生成的唯一标识相当于二维码的身份证。轮询到第 3 个状态时说明用户已经在手机上点了确认服务端返回 token我们把 token 写进 Session 的 Authorization 请求头。之后所有库存查询和下单请求都会自动带上这个 token。二维码方案的优点是登录态稳定缺点是必须用手机扫码自动化程度略低。如果你更希望全自动可以把登录方式改成短信验证码# core/login.py 中的短信验证码登录 def login_by_sms(session, phone): session.post(API[sms_send][1], json{phone: phone}) code input(请输入短信验证码: ) resp session.post(API[sms_login][1], json{phone: phone, code: code}) token resp.json()[data][token] session.headers[Authorization] fBearer {token}短信登录适合在脚本跑之前手动确认一次登录态然后把 token 保存到本地文件下次启动直接读取。注意不要把手机验证码写死在配置里验证码是一次性的写死没有任何意义。token 保存到本地文件时要设置文件权限避免其他用户读到。3.2 倒计时监控与库存轮询抢票的核心不是下单而是“在正确的时间点发起首次查询”。如果查询太早接口还在锁仓查询太晚票可能已经被别人锁定。所以 StockMonitor 要先算倒计时再动态调整轮询频率。# core/monitor.py import time import logging from utils.timeutil import now_ms class StockMonitor: def __init__(self, session, config, on_stock): self.session session self.activity_id config[activity_id] self.target_sku config[sku_id] self.low_interval float(config[low_interval]) / 1000 self.high_interval float(config[high_interval]) / 1000 self.stop_flag False self.on_stock on_stock def run(self): wait_sec self.countdown_sec() if wait_sec 0: time.sleep(max(0, wait_sec - 0.5)) # 提前 500ms 进入高频轮询 while not self.stop_flag: start now_ms() try: stocks self.fetch_stock() for sku in stocks: if sku[sku_id] self.target_sku and int(sku[stock]) 0: self.on_stock(sku) break except Exception as e: logging.warning(库存查询失败: %s, e) elapsed now_ms() - start interval self.high_interval if self.countdown_sec() 0 else self.low_interval time.sleep(max(0.05, interval - elapsed / 1000))countdown_sec()计算的是当前时间到sale_start的秒数内部会使用时间校准后的偏移量。low_interval是开票前的低频轮询间隔比如 2000mshigh_interval是开票后的高频轮询间隔比如 200ms。这样做的原因是开票前频繁请求会产生很多无意义流量还可能被平台限流只有临近开票时才需要加速。now_ms()返回的是校准后的毫秒时间戳每次轮询记录开始时间轮询结束后用固定间隔减去实际消耗时间避免 sleep 叠加导致周期越来越慢。这是很多抢票脚本最容易忽略的细节直接用time.sleep(0.2)但网络请求本身可能已经耗时 300ms真实的轮询周期就变成了 500ms开票瞬间的竞争力大打折扣。3.3 订单提交与幂等保护下单是最需要谨慎的部分因为一旦重复提交平台可能直接限制当前账号。我的做法是在内存里维护一个已提交订单集合并用 threading.Lock 保证多线程环境下不会同时写入。# core/order.py import threading from storage.db import save_order_to_db ORDER_LOCK threading.Lock() submitted_orders set() def submit_order(session, activity_id, sku_id, count): payload { activity_id: activity_id, sku_id: sku_id, count: count, } with ORDER_LOCK: if (activity_id, sku_id) in submitted_orders: return None, already_submitted resp session.post(API[order_create][1], jsonpayload) rj resp.json() if rj.get(code) 0: order_id rj[data][order_id] submitted_orders.add((activity_id, sku_id)) save_order_to_db(activity_id, sku_id, order_id) return order_id, ok return None, rj.get(msg)这里的关键点是ORDER_LOCK。假设未来你扩展成多线程监控多个票档两个线程同时发现库存有票如果不加锁两个线程都会发起下单结果要么生成了两个订单要么被平台判定为并发异常。加锁之后同一个(activity_id, sku_id)只会第一次提交成功。count是购票数量一般不超过平台限购数。提交成功后返回order_id数据库里保存这条记录后续可以继续调用order_check查询订单状态。如果订单创建失败返回的msg会被记录到日志里不会让脚本静默失败。还有一点值得注意很多平台创建订单并不会立刻锁定库存而是有 2 到 5 分钟的支付窗口。也就是说下单成功不代表抢票成功必须在收到推送后尽快打开支付链接完成支付。这部分属于人工动作脚本最多帮你把支付链接拼出来不应该去自动调用支付接口。4. 避坑与常见问题排查四个最容易翻车的环节抢票脚本最大的特点就是平时跑得好好的一到开票瞬间就翻车。我把实际开发里遇到最多的四个问题整理成避坑记录每一条都按“现象 → 原因 → 解决”来写方便你照着排查。4.1 登录态频繁失效轮询几分钟后接口返回 401现象脚本刚启动时库存查询正常跑了不到 5 分钟所有接口开始返回 401登录态丢失。原因这是典型的请求头不一致导致。很多开发者会在每个请求里动态换 User-Agent觉得这样更“真实”。但票务平台的 Session 校验会记录登录时的 User-Agent后续请求如果突然变成另一个值就会被判定为账号异常强制下线。另一个常见原因是本地保存了旧 token但脚本启动时又触发了一次新登录导致旧 token 被服务端作废。解决把请求头固定在一个地方。core/session.py里创建 Session 时统一设置 User-Agent、Referer、Origin之后所有请求都复用同一个 Session不要每次请求再动态修改。如果你手动保存了 token启动时先检查 token 是否还有效再决定是否重新登录。从那以后我每次写网页端自动化脚本都会把“Header 一致”当成第一优先级而不是先去优化请求速度。4.2 开票后接口一直返回 0 或 -1页面却显示有票现象倒计时归零后脚本立刻查库存接口返回stock: -1但手动刷新页面能看到还有余票等脚本反应过来时票已经没了。原因本地时间和平台服务器时间有偏差。很多抢票工具只盯着本地零点但平台的“开票时间”以服务器时间为准。如果本地时钟慢了两三秒脚本会在真正开票前就发起查询接口自然还没放票。解决在项目里加入sync_time命令启动时请求一次平台时间接口计算 offset。utils/timeutil.py里的核心逻辑就是把 offset 缓存下来之后所有倒计时都用local_time offset来计算。为了让结果更稳开票前 30 秒再校准一次避免运行过程中 offset 漂移。这个改动看着小实际对抢票成功率影响最大。4.3 同一个订单被重复提交账号被限制下单现象开票瞬间脚本对一个 sku 提交了 3 个订单平台弹出“操作过于频繁”后续所有下单请求都被拒绝。原因没有做幂等保护。前端页面点击一次购票按钮后按钮会被置灰但脚本里的 while 循环不会自己停下来。如果on_stock回调没有判断是否已经下单每轮库存查询只要发现还有票就会再提交一次。解决使用 3.3 节里的ORDER_LOCK和submitted_orders集合。下单前先检查当前activity_id sku_id是否已经在集合里已存在就直接跳过。同时把订单写入 SQLite哪怕脚本重启也能通过查单接口确认是否真的下过单而不是盲目重试。幂等保护是抢票项目的安全底线不能省。4.4 某些接口单独请求 404浏览器里却正常现象手动在浏览器点购票没问题但脚本请求创建订单接口时返回 404或者变成 403。原因接口校验了 Referer 和 Origin。很多网页端接口不能直接裸请求必须带上来源页面地址和跨域来源。如果你只复制了 URL没有复制请求头服务端会因为缺少来源信息拒绝服务。解决从浏览器 Network 面板里找到创建订单的请求右键复制为 cURL再转换成 Python requests 代码。重点保留Referer: https://xxx.com/activity/123456和Origin: https://xxx.com这两个头。在api/endpoints.py里把公共请求头统一维护好而不是在每一次请求里重复传能少踩很多坑。5. 运行与部署从依赖安装到后台常驻代码写完之后要让它稳定常驻而不是手动点运行。这一章讲实际启动步骤包括环境准备、配置文件、命令行参数和后台任务挂载。5.1 环境准备与依赖安装项目依赖很少核心只有 requests 和 colorama。colorama 只是为了在终端里把日志高亮显示删掉也不影响核心逻辑。在项目目录下执行cd ticket_bot python -m venv .venv source .venv/bin/activate pip install -r requirements.txtrequirements.txt 内容如下requests colorama不要小看虚拟环境这一步。很多坑来自系统 Python 环境太乱requests 版本被全局包影响导致 Session 行为异常。用 venv 隔离之后至少能保证依赖版本一致。如果你用的是 Windows激活命令是.venv\Scripts\activate后续步骤一样。5.2 配置文件说明所有可调参数都放在 config.ini 里避免为了修改一个时间参数去翻代码。示例[account] # 二维码登录不需要密码这里只留手机号 phone [activity] activity_id 123456 sku_id 654321 count 2 [time] sale_start 2025-01-01 20:00:00 [request] low_interval 2000 high_interval 200 timeout 3activity_id是活动 IDsku_id是具体票档 ID这两个都需要从浏览器开发者工具的 Network 请求里复制。count是购票数量设置为 2 代表一次购买两张。low_interval是开票前的轮询间隔单位毫秒high_interval是开票后的轮询间隔单位毫秒。timeout是单个请求的超时时间设太短容易误判失败设太长又会拖慢整个循环一般 3 秒即可。如果你要监控多个场次常见做法是复制多个 config 文件比如config_a.ini、config_b.ini然后分别启动进程而不是试图在一个进程里处理所有逻辑。多进程隔离更稳妥某一个挂掉不会影响其他。5.3 启动方式与命令行参数main.py 支持三个参数# 先登录一次确认登录态 python main.py --config config.ini --mode login # 校准本地时间 python main.py --config config.ini --sync-time # 进入监控和下单模式 python main.py --config config.ini --mode monitor第一次使用建议先跑登录模式扫码成功后不要关闭终端直接切另一个终端跑 monitor。如果你希望登录 token 保存下来可以在core/login.py里把 token 写入本地文件并在启动时读取。这里我不展开因为你只要让 monitor 模式和登录模式在同一个终端会话里requests Session 的 Cookie 是自动保留的。monitor 模式运行后日志会持续输出时间、库存、请求耗时。如果发现请求耗时超过 1 秒就要考虑网络环境或接口地址是否正确。开票瞬间日志会很密集建议把所有输出追加到文件方便事后分析。5.4 后台常驻与查看日志个人电脑上最简单的方式是 nohupnohup python main.py --config config.ini --mode monitor bot.log 21 这样终端关闭后进程还在跑日志写入 bot.log。查看实时状态用tail -f bot.log如果你在服务器上更推荐用 systemd 管理。创建/etc/systemd/system/ticket-bot.service[Unit] DescriptionTicket Auto Buy Bot Afternetwork-online.target [Service] ExecStart/opt/ticket_bot/.venv/bin/python /opt/ticket_bot/main.py --config /opt/ticket_bot/config.ini --mode monitor Restartalways Userticket [Install] WantedBymulti-user.target然后执行systemctl enable --now ticket-bot。这里的 User 必须是有权限访问项目目录的普通用户不要用 root 跑。Restartalways可以保证进程意外退出后自动拉起。6. 二次开发给脚本加一个更稳的请求节流器如果你想在拿到这份资源后继续改造我建议第一件事不是去优化下单速度而是给所有请求加一个统一节流器。抢票脚本最容易犯的错就是“想更快于是疯狂发请求”结果反而被平台限制得不偿失。把速度控制在一个合理区间才是能长期跑的方案。我一般会在 utils/retry.py 里加这样一个装饰器import time import threading def min_interval(seconds): lock threading.Lock() last_call 0.0 def decorator(func): def wrapper(*args, **kwargs): nonlocal last_call with lock: now time.time() wait last_call seconds - now if wait 0: time.sleep(wait) last_call time.time() return func(*args, **kwargs) return wrapper return decorator用法很简单在库存查询方法上标注min_interval(0.2)就能保证这个函数两次调用之间至少间隔 200ms。相比在循环里手动 sleep这个装饰器的好处是集中管理代码里不会散落一堆时间计算。后续想调整频率只需要改装饰器参数。第二个值得改造的方向是把账号抽象成类。现在大多数函数都直接接收 session 和 config一旦要支持多账号并发函数签名会越来越乱。我的做法是定义TicketAccount里面封装 session、config、submitted_orders所有登录、监控、下单都变成这个类的方法。改造之后多账号并发只需要创建多个对象再用 ThreadPoolExecutor 调度。第三个方向是增加备用票档降级逻辑。比如想买 680 元档但开票后 2 秒内库存为 0可以自动尝试 880 元档。注意这个降级逻辑必须加上一次性开关并且在下单前提醒用户避免用户预算超了都不知道。这些改进全部是基于“稳”的原则。从那以后我每次写网页端自动化脚本都会先确认三件事请求限速做好了吗幂等去重加上没有时间校准跑通了吗。这三件事没确认我不会让脚本进入真正的抢票环节。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询