☰
Python抢票脚本三步实战:从HTTP请求到异常处理
2026/10/21 8:44:30 网站建设 项目流程

很多刚学Python的朋友问过我同一个问题:票务平台的自动抢票脚本到底是怎么写的。这类需求确实是Python自动化里最有吸引力的练手项目之一,它不复杂,但能把你平时学的requests、Cookie、并发、异常处理全部串起来,跑通了之后成就感相当强。这篇文章就围绕这个场景,走一遍三步上手的完整流程,把每一步背后的原理讲明白,也把常见的坑提前帮你踩一遍。

先说清楚适用范围:这里讨论的是用Python脚本去处理某个票务平台(演出票、活动票类网站)的公开购票流程,重点在技术原理和自动化实现。脚本本身是一条“自动化操作链路”,不是去破解系统,也不是攻击服务器。实际使用前一定要看目标平台的服务条款,个人用来学习技术是合理的,拿去刷票、囤票、绕过平台风控就不合适了,这点心里要有数。

1. 抢票脚本到底解决了什么问题

1.1 手动抢票的痛点在哪里

如果你手动抢过热门演出的票,应该对下面这套操作不陌生:提前几天研究开票时间,快到点时不停刷新页面,好不容易界面加载出来,选择场次、票档、张数,填好观演人,再点提交订单,最后跳出来“库存不足”或者“您来晚了”。整个过程全靠手速和网速,而且那几十秒里人高度紧张,非常容易点错。

拆解一下,手动抢票的瓶颈主要有三个。

第一个是刷新频率。人眼关注一两个页面还行,开票瞬间多个场次、多个票档一起变状态,根本看不过来。脚本可以用循环去轮询接口,几百毫秒一次也不累。

第二个是操作链路长。从刷新到提交成功,中间要刷新库存、读取场次、传入观演人、生成订单,每步都是界面上的点击和表单操作,人的点击速度是几百毫秒级别,脚本提出一个HTTP请求通常几十毫秒就能完成,差距非常明显。

第三个是对状态的响应速度。手动刷新会发现页面有缓存,你看到的库存状态可能滞后好几秒。直接请求后端接口拿到的是第一手数据,更接近真实库存。

明白了这三个痛点,你就知道脚本不是在“作弊”,它只是把一个重复的、要求反应速度的操作流程自动化了。理解了这一点,后面设计脚本的逻辑就顺理成章了。

1.2 自动化的核心目标:快、准、稳

抢票场景下的自动化,追求的不光是“快”。“快”只是其中一方面,还要“准”和“稳”。

“准”指的是操作不能出错。手动操作时选错票档、填错观演人是常见失误,脚本只要逻辑写对了,每次请求的参数都一致,不容易出现低级错误。但脚本有个反面问题:如果目标场次不存在了,或者参数写死,就容易请求失败。所以脚本里要有校验逻辑,每步请求前先确认前置条件。

“稳”指的是整个链路要能跑完。开票瞬间服务的压力很大,接口超时、连接重置是家常便饭,脚本如果没有重试逻辑和异常处理,第一层请求失败就直接退出,那还不如手动。所以很多抢票脚本真正难写的地方不是“请求库存”这个动作,而是整套异常处理和状态判断的工程能力。

一个好的抢票脚本,本质上是一个带状态感知的自动化请求客户端:它知道现在是几点几分,知道目标场次可不可以买,知道订单提交成功了没有,也知道失败后该怎么重试。把这个目标想清楚,后面三步走的方向就不会偏。

2. 三步上手的整体思路设计

2.1 三步法怎么划分

把整个流程压缩成三步,是为了让新手不至于一上来就被细节淹没。很多人看到网上流传的抢票脚本源码,第一反应是“怎么这么多代码”。其实那些代码再复杂,也能归进三个环节:

第一步,摸清抢票流程对应的HTTP请求。这是所有工作的基础。你得知道点击“立即购买”后,浏览器到底向后端发了什么请求,带上了哪些参数。

第二步,写一个能跑通核心链路的脚本。先不管验证码、加密参数这些复杂的东西,把“查询库存 -> 创建订单 -> 提交订单”这条主链路跑通,哪怕慢一点,先看到一次成功。

第三步,补齐健壮性与并发能力。加上自动重试、请求频率控制、验证码处理、结果通知等。这步做完,脚本才能从“能跑”变成“能用”。

三步不是简单的流水线,第二步做完往往要回过来改第一步的抓包结论,因为有些参数在真实请求里才会暴露出来。我见过不少新手一上来就研究滑块验证码和JS逆向,核心链路还没通,最后卡在奇怪的地方。正确顺序永远是先打通主流程再折腾边缘环节。

2.2 方案选型:模拟请求还是浏览器自动化

写抢票脚本,通常有两套技术路线:纯HTTP请求模拟和浏览器自动化。

纯HTTP请求模拟是直接用requests库去调用后端接口。优点是非常快、资源占用低,可以用Session保持登录态,配合多线程或异步可以做到很高的请求频率。缺点是如果目标站点的接口有复杂的签名参数、加密逻辑或者频繁插入验证码,代码复杂度会直线上升。

浏览器自动化则用Selenium、Playwright这类的工具,让一个真实的浏览器实例代替人去点击操作。优点是几乎不关心接口怎么加密、参数怎么生成,只要模拟用户行为就行,对付滑块验证码也天然有优势——人怎么拖,它就怎么拖。缺点是慢,单个浏览器实例每秒能完成的操作次数远低于HTTP请求,而且并发要开多个浏览器,对机器资源要求高。

我的建议是以HTTP请求为主,以浏览器自动化作为补充。比如登录环节如果出现滑块,用浏览器自动化单独处理;核心的下单请求还是走HTTP,速度和稳定性更有保障。这也是大多数成熟脚本的架构思路:能用接口解决的用接口,接口解决不了的,用浏览器补位。

第一步的抓包,目的就是判断这个平台到底适合哪种路线。有些票务系统的接口是纯JSON格式,参数一目了然,那写起来就很舒服;如果发现请求头里有加密签名,且每次请求签名都在变化,那就要做好啃JS逆向的心理准备,或者干脆考虑换浏览器自动化的路径。

3. 核心细节与实操要点

3.1 HTTP层面的关键字段

不管目标平台长什么样,抢票流程里一定有几个绕不开的HTTP字段,务必要理解它们的含义。

User-Agent是第一道门槛。很多平台会识别请求是不是来自浏览器,如果你用Python默认的UA去请求,很容易被拦下来。抓包时从DevTools的Network面板里复制真实的UA,写进脚本里。顺手把Referer也补上,很多接口会校验来源页面。

Cookie是整个登录态的载体。你需要让脚本像一个已经登录的用户,最简单的方式是把浏览器里的Cookie复制出来,塞给requests.Session。但Cookie有时效性,长期使用的脚本要处理过期问题。进阶做法是脚本自己走一遍登录接口,用短信验证码换取Cookie,这样每次都能拿到新鲜凭证。

签名参数是最棘手的部分。很多平台为了防自动化,会在请求参数里加入一个根据固定规则计算出来的签名,内容通常是时间戳、固定盐值、当前请求参数拼接后取哈希。遇到这种情况,要先尝试在浏览器里搜索参数名,看能不能直接找到生成代码;如果找不到,可以考虑用浏览器自动化执行相关的JS代码来生成签名。这里记住一个原则:签名算法的目的是增加自动化成本,不是绝对安全,所以不要怕,多抓几次请求对比参数规律。

请求方法里还有一个容易被忽略的点:Content-Type。POST请求用表单格式还是JSON格式,后端接收方式不同。如果你抓包时看到请求体是一串JSON字符串,那脚本里就要用json=参数;如果看到的是key=value&key2=value2,那就是表单格式,用data=参数。搞错了会返回参数错误,这是新手遇到频率最高的报错之一。

3.2 登录态与会话保持

登录态处理得好不好,决定了脚本能走多远。

最省事的方式是手动登录,提取Cookie。打开浏览器开发者工具,登录目标站点,然后在Network面板找到任意一个请求,从Request Headers里复制完整Cookie字符串。在脚本里直接设置到requests.Session的headers里,二十行代码就能让脚本拥有登录身份。这种方式适合自己临时用,缺点显而易见,Cookie失效就要重新复制。

复杂一点的方式是脚本自动登录。票务平台的登录往往会经过短信验证码、图片验证码等环节,自动化登录非常容易触发风控。我见过一些脚本的设计是先手动登录获取Cookie,然后保存到本地文件,下次运行时优先读取文件里的Cookie;如果发现请求返回未登录,就提示用户重新登录。这种方式兼顾了灵活性和稳定性,推荐新手采用。

会话保持还与Session对象密切相关。requests的Session会自动保存服务端返回的Cookie,后续请求自动带上,模拟了同一个浏览器实例在不同请求间保持会话的效果。有些同学每次请求都新建一个Session,那和每次重新打开一个未登录浏览器没区别,下单必然失败。记住:整个抢票流程,全程使用同一个Session对象。

3.3 验证码与滑块的处理思路

验证码是抢票自动化绕不过去的话题,平台之所以在关键时刻放验证码,就是要拦截自动化脚本。

先要明确:验证码不是所有场景都会出现。很多平台在普通查询阶段不验证,只有在下单提交前才弹出。所以脚本设计上可以先用无验证码模式跑通流程,再针对触发点单独处理。

滑块验证码是当前最普遍的。处理方案有三种。第一种是直接人工介入:脚本运行到验证码出现时,用某种方式通知你(比如发送一个窗口提醒或消息),在浏览器里手动滑一下,之后脚本继续跑。第二种是模拟拖动:用Selenium或Playwright定位滑块,按人手的轨迹去拖动。这需要注意轨迹的随机性,直线匀速拖动会被识别成机器,要加入加速减速和轻微上下抖动。第三种是调用第三方打码平台,准确性高,但要花钱且有泄露风险,个人学习不建议。

文字类验证码可以用OCR思路,识别成功率取决于字体扭曲程度。许多平台的验证码都是计算题或成语题,单纯OCR不够,还得做语义理解,这个复杂度已经超过抢票本身了。所以我的建议始终是:优先做成人工辅助验证,把自动化投入在更有价值的请求逻辑上,验证码环节交给人工处理,这样成功率最高。

3.4 并发策略与请求节奏

抢票场景里,并发是个需要仔细设计的点。很多人以为线程开得越多越好,结果开票瞬间发出去几百个请求,直接把账号封了。调速比提速更重要。

先说简单的模型。假设你抢一个热门场次,脚本启动后有两种工作模式:开票前轮询模式和开票后提交模式。开票前,脚本以固定间隔查询库存状态,比如每500毫秒查一次,这个频率低,不容易触发风控。一旦发现目标场次变为可售,立刻进入提交模式:先创建订单,再提交订单,整个过程只发几次请求,频率仍然不高。

那并发体现在哪里?体现在“提前准备好多个任务”上。比如你同时关注多个票档,可以用一个线程池并行监控;再比如你要抢多张票,可以提前准备好观演人列表。真正的抢票行为本身不需要高并发,它需要的是低延迟响应。所谓低延迟,就是定时轮询的间隔要短,且每次轮询的请求要轻量,最好用最精简的接口参数。

有些极端场景,平台接口在开票前是关闭的,轮询会一直返回无效数据,这时候频繁请求只会增加封号风险。正确的做法是设置轮询窗口:开票前5分钟开始轮询,开票前1分钟提高频率,开票瞬间全力请求。窗口之外的请求次数尽量降为零。

请求节奏还有一个细节:请求超时和重试之间的时间差。如果接口超时设置为30秒,而轮询间隔是500毫秒,当一次请求卡住时,后面的请求都会堆积。所以每轮轮询之前要检查上一轮是否结束,同时把超时时间调短,比如连接3秒、读取3秒,超时了立即重试而不是傻等。

4. 实战实现:从抓包到跑通第一版

4.1 第一步:抓包定位关键接口

具体操作很固定:打开一个无痕浏览器窗口,登录目标票务平台,打开开发者工具,切到Network面板,勾选Preserve log保留日志。然后从头到尾手动操作一遍购票流程,哪怕最后不支付,也要走到提交订单那一步。

操作完后,Network面板里会留下几十个请求。要筛选出和购票相关的接口,诀窍是看请求URL里的关键词,比如包含order、ticket、stock、deal之类的接口名。把每个XHR请求的请求头、请求体、响应体都过一遍,在表格里记下接口的URL、方法、参数和用途。

我以一个典型的场次查询为例,说明抓包之后要提炼出什么:

接口用途请求方法关键参数响应关键字段
获取演出详情GETid(演出ID)场次列表、可售状态
获取票档库存GETitemId、skuId库存数量、价格
创建订单POSTitemId、skuId、num订单号
提交订票人信息POSTorderId、观众信息提交状态
确认支付POSTorderId、支付方式支付链接

不用把全部接口都整理出来,先抓核心链路:查库存、创建订单、提交订单。这三个接口能跑通,整个脚本就已经成功了一半。如果某些接口的响应体里有加密内容,先去浏览器里看渲染后的页面数据,那往往就是解密后的结果。

这一步产出最关键,后面所有代码都围绕这份接口清单展开。中途发现某个接口参数很复杂,先别急着硬刚JS逆向,观察一下是否能在前一个接口的响应里直接取到。很多参数是服务端算好返回的,不需要前端动态生成。

4.2 第二步:写一个能跑通的主流程脚本

拿到接口清单后,先用Python写个最简版本的脚本,目标只有一个:从查询库存一路走到提交订单,哪怕流程很笨拙。

可以这样组织代码。首先准备好请求基础设施,包括Session对象、请求头、Cookie和基础URL配置:

import requests import time BASE_URL = "https://example-ticket.example.com" UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..." cookies_str = "你的Cookie字符串" session = requests.Session() session.headers.update({ "User-Agent": UA, "Referer": BASE_URL + "/", "Origin": BASE_URL, }) session.cookies.update(parse_cookie(cookies_str))

接着实现库存查询。这个函数要轻量,只请求最精简的接口,并返回可售状态:

def check_stock(session, item_id, sku_id): url = f"{BASE_URL}/api/v1/item/{item_id}/sku/{sku_id}/stock" resp = session.get(url, timeout=3) data = resp.json() return data.get("status") == 1 and int(data.get("stock", 0)) > 0

然后实现创建订单和提交订单。创建订单通常需要把场次、票档、数量传给后端,后端返回一个订单号。提交订单要把观演人信息带上去:

def create_order(session, item_id, sku_id, num=1): url = f"{BASE_URL}/api/v1/trade/order/create" payload = { "itemId": item_id, "skuId": sku_id, "num": num, } resp = session.post(url, json=payload, timeout=5) return resp.json()["orderId"] def submit_order(session, order_id, audience_list): url = f"{BASE_URL}/api/v1/trade/order/submit" payload = { "orderId": order_id, "audience": audience_list, } resp = session.post(url, json=payload, timeout=5) return resp.json()["success"]

主流程循环可以非常朴素:先循环查库存,发现可售后创建订单并提交:

def main_loop(): while True: if check_stock(session, ITEM_ID, SKU_ID): order_id = create_order(session, ITEM_ID, SKU_ID) ok = submit_order(session, order_id, AUDIENCE_LIST) if ok: print("提交成功,订单号:", order_id) break else: print("提交失败,继续重试") time.sleep(0.5)

这版脚本不考虑验证码,不考虑异常,不考虑签名参数,但它足以让你确认一件事:这个方案在原理上能不能走通。如果跑起来,库存查询没问题,但下单时返回错误,那就说明参数不完整或者签名缺失。这时候回去看抓包记录,把缺失字段补上。

核心原则是“先跑起来再优化”。很多新手第一步就想写一个支持多线程、自动过验证码的终极版本,结果代码写了一千行,跑起来全是问题。用最短路径验证可行性,是工程里最重要也最容易被忽略的方法。

4.3 第三步:加上异常处理、重试与通知机制

第一版跑通后,脚本还远没到能用程度。开票那一刻,接口随时可能超时、返回繁忙,或者莫名其妙地弹验证码。下面几个模块就是第三阶段要补的。

异常处理是整个脚本的骨架。不要在脚本里用裸的requests.get(),所有网络请求都要包裹try/except。捕获到的异常要区分类型:超时连接错误可以重试,服务端返回5xx可以短暂等待后重试,4xx错误大概率是参数或风控问题,重试没有意义,应该记录日志并停下来。

重试机制要有退避策略。比如首次失败等待300毫秒,第二次失败等待500毫秒,第三次失败等待1秒,不要无限重试同一个失败请求。一般设计三层重试:连接层超时重试、接口状态校验重试、订单提交失败重试。每一层的重试次数都要有限制,避免开票高峰时脚本陷入死循环。

结果通知是很多初学者容易漏掉的。脚本在后台跑,如果成功了你不能一直盯着屏幕。可以在脚本里加一个通知模块,订单提交成功后把订单号发到即时通讯工具里;失败次数多了也要通知,让你及时介入处理验证码。通知实现不复杂,本质就是发一个HTTP请求到某个Webhook地址。

这个阶段还应该加一个状态机。把脚本的运行状态抽象成几个阶段:轮询中、已创建订单、提交中、已完成、失败。每个阶段有明确的进入条件和退出条件,这样脚本的可读性和可维护性会提升很多。我见过一个连状态管理都没有的抢票脚本,功能确实能跑,但一旦出错,日志完全看不出它现在到底在执行哪一步,很难排查问题。

5. 常见问题与排查技巧实录

5.1 高频踩坑点整理

这一节我把实际调试过程中遇到过的高频问题整理成一张速查表,每个问题都给出定位方向和解决办法。

现象可能原因排查/解决方向
请求返回403User-Agent缺失或异常补全浏览器UA,检查Referer
请求返回468或418触发风控限制降低请求频率,改用浏览器自动化
提示未登录Cookie过期重新抓取Cookie,检查Session是否一致
参数错误Content-Type或参数格式不对对比抓包里的请求体格式
库存明明有但下单失败请求缺少签名参数回去分析JS,或者补全加密字段
下单成功但无法支付事务ID或订单号未正确传递检查前后接口的参数关联
开票后一直超时服务端压力大或连接池耗尽调短超时时间,增加重试退避
验证码频繁弹出请求频率过高被标记拉大轮询间隔,减少并发

这里最值得说的是第二条的468或418这类状态码,这属于平台主动拦截。这种时候不要用“更暴力”的请求来对抗,正确的做法是让脚本休息一段时间,模拟正常用户的操作节奏。我之前调试某个票务平台的接口时,开票前每分钟查询一次没问题,有一次为了测试把频率调到每秒5次,十分钟后账号所有请求都返回异常状态。休息半小时后才恢复。这个教训说明,脚本设计里一定要有自我保护机制,在高频请求连续失败N次之后,自动暂停一段时间。

还有一个新同学很容易踩的坑是Cookie管理混乱。有些人既是浏览器里登录着,又用curl命令测试接口,然后发现有时能请求成功,有时不能。原因往往是命令行工具没有带上浏览器里的Cookie。推荐的做法是:把Cookie统一保存在一个文本文件里,脚本启动时读取,需要更新时手动替换,只维护这一个入口。

5.2 如何判断脚本到底卡在哪一步

脚本跑不通的时候,很多人第一反应是看代码哪里写错了。实际上,抢票脚本大部分调试时间不是在改代码,而是在定位问题发生在哪个环节。

最有效的手段是完善日志输出。在库存查询、创建订单、提交订单、异常捕获这四处都加上带时间戳的日志。日志要包含请求的URL、参数、返回状态码和响应摘要。写日志的时候不要只输出“请求失败”,要把抛出的异常类名和错误信息一起打出来。有了完整的日志,排查问题就是看时间线,而不是靠猜。

判断思路是这样的:如果库存查询正常但创建订单报错,问题大概率在参数拼装或签名逻辑;如果创建订单成功但提交订单报错,问题可能在订单状态未满足条件;如果所有接口都返回超时,问题在网络环境或目标服务端压力,与代码关系不大。按这个方向逐层缩小范围,通常十分钟内能找到根因。

有些平台的前端有动态Token。所谓动态,就是每次页面加载时,服务端会下发一个有时效性的加密串,前端请求时必须携带。动态Token过期后,接口会返回类似“权限不足”的响应。这种问题单看接口日志不好发现,需要对比两次请求之间的Token值是否有变化,如果变了,就要在设计上定期刷新Token。

另一个隐蔽问题是本地时间和服务器时间不一致。部分签名算法会把时间戳作为参数,如果你的机器时间快了或慢了超过几分钟,请求就会被拒绝。遇到时间戳相关报错时,最好先同步一下系统时间,再排查其他原因。

5.3 验证码介入之后的人工配合流程

很多脚本选择人工辅助处理验证码,这里有个值得分享的细节:模拟请求和浏览器操作如何无缝衔接,是个容易忽略的难点。

建议这样设计。入口还是Python脚本,负责轮询、创建订单和提交订单,但在发现需要验证码时,停止自动提交,立即打开一个浏览器实例,自动定位到验证码弹窗,并发送通知消息给你。你只需要在浏览器里手动完成验证,脚本检测到验证成功后再继续。

实现的关键是“检测验证是否成功”。可以通过轮询一个接口的返回状态来实现,比如验证完成前,创建订单接口会持续返回“需要验证”的错误码;一旦你在浏览器里通过了验证,服务端会话状态更新,接口再调用就会正常返回。脚本只需要每隔1秒重新提交一次创建订单请求,直到它成功为止。

这种方案的稳定性和用户体验都很好,既有脚本的速度和可靠性,又保留了人应对复杂验证码的能力。我建议你在写抢票脚本的时候,把“人工介入”当成一个正式功能来设计,不要等出问题再去补。

6. 一步到位的优化技巧分享

到这里,三步走已经讲完了,但有几个小技巧能让脚本更耐用,顺手分享出来。

第一个技巧是接口查询参数要尽量精简。抢票峰值时,每一次请求都会给服务器造成压力,也给自己带来风险。用最少的参数、最快的接口完成库存判断,而不是用完整页面数据接口。比如只请求/sku/stock这个轻量接口,就不要去请求/detail?include=all这类的重量级接口。响应数据越小,请求速度越快,对服务的扰动也越小。

第二个技巧是巧用离线数据缓存。有些平台的演出详情、场次列表并不是频繁变化的。开票前可以把这些数据抓下来存到本地JSON文件,轮询时直接读本地文件,只有库存状态实时请求。这样既减少了请求量,又保证了流程不中断。

第三个技巧是准备好预案列表。抢票前把多个票档按优先级排好,比如最低价优先、时间最早优先、或者指定座位范围优先。脚本发现最高优先级票档无票时,自动尝试下一个。这个逻辑实现起来不复杂,却能让脚本在真实场景里的成功率上升一个台阶。

第四个技巧是预留扩展现的空间。比如支付环节,很多平台要求跳转到支付页面,如果脚本能在提交订单后自动打开浏览器并帮你跳转到支付二维码页面,你只需要扫码,体验会非常连贯。这同样需要手动浏览器和HTTP请求的配合,思路跟验证码处理类似,值得一试。

我个人在实际调试中的体会是,抢票脚本这类自动化项目,最大的门槛从来不是Python语法,而是“把流程拆成一系列可验证的请求”这种工程思维。很多人一开始盯着验证码和加密参数这些难关,反而忽略了“先跑通主流程”这一基本原则。只要你按三步走来,每一步都留好日志和验证节点,哪怕第一次跑通用了半天,第二次再遇到类似票务自动化项目,半天就能摸完整个链路。

最后再提醒一句:这些方法只能用于学习交流,目的是理解接口自动化的通用原理。真实购票请通过平台官方渠道,按平台规则操作。技术本身没有对错,怎么使用才是关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询