简介面向具备一定Python基础的开发者和技术学习者这套压缩包完整收录了B站会员购漫展抢票自动化项目的源码、配置与示例脚本。项目以纯接口交互为主线并提供图形化界面封装同时专门设计了验证码预演练习模块便于理解自动登录、商品监控、下单提交等核心链路的实现方式多种验证码处理器的集成也为研究滑块、点选等常见验证机制的识别与处理逻辑提供了实践切口。压缩包共66个文件、约19.54MB以Python源码为主要组成辅以sample示例、YAML/文本配置、Dockerfile部署文件、说明文档及图标资源目录划分与注解清晰既适合逐模块阅读调试也可直接作为二次开发骨架。目前已有91人浏览学习。建议在法律和平台规则允许范围内将其作为接口分析、验证码识别与自动化流程设计的练习项目而非用于影响公平性的真实抢票。1. 图形化抢票脚本在练什么纯接口下单与验证码预演的双重实战B站会员购抢漫展门票放票瞬间的接口压力、验证码兜底和登录态失效恰好是接口编程里最真实的一组样本。这个bilibili图形化抢票脚本核心是纯接口方案不依赖浏览器自动化直接用HTTP请求走完会员购下单链路再用本地验证码预演把滑块识别的通过率调到能信任的状态。适合想把接口编排、桌面端UI封装和图像识别三块串起来的开发者。前提是把它当练习项目用来练手接口时序、风控处理和验证码工程化不是拿去当黄牛工具。2. 会员购纯接口方案从登录态到下单链路的最小闭环2.1 会员购下单要走的接口链路纯接口方案的第一步是把会员购的下单链路拆清楚。用浏览器开发者工具打开一个漫展活动详情页从进入活动页到提交订单网络面板里能看到一组JSON接口在按顺序协作。链路大致是活动信息 → 场次列表 → 票档详情 → 创建订单 → 订单确认。每个环节之间有严格的参数依赖前一环的返回值要作为后一环的入参。环节接口形态关键参数返回要点活动信息/api/ticket/activity/list活动id场次列表、活动状态票档详情/api/ticket/activity/detail活动id票档sku数组、库存数量创建订单/api/ticket/order/createitem_id、sku_id、num订单id、待支付金额订单确认/api/ticket/order/confirmorder_id支付状态、支付链接表里的接口路径是练习时抓包得到的参考形态B站的接口更新频率不低每次动手前花两分钟重新抓一次包对齐路径。凡是返回体里code ! 0的把message和data字段完整打进日志后面排查问题全靠这两个字段不要在日志里只打印code数字。这块的练习价值在于接口有状态依赖。比如创建订单之前你得先从活动详情接口拿到sku_id和当前库存而详情接口的库存是页面加载时生成的快照放票瞬间它的值和下单接口拿到的实时库存存在时间差。纯接口抢票脚本的核心难点不是某个接口调得有多快而是把「探测库存」和「提交订单」两个动作的时序控制好这正是第三章的轮询循环要解决的问题。选择纯接口而不是Selenium这类浏览器自动化原因有两个。一是接口方案的请求开销小不需要拉起完整浏览器内核轮询频率可以做到亚秒级二是接口响应是结构化JSON库存字段、订单号、错误码都能直接解析进代码逻辑。代价是接口一旦变动需要重新抓包适配而且请求特征容易被风控识别所以请求头和频率控制反而比浏览器方案更讲究。2.2 登录态获取扫码登录与Cookie持久化纯接口方案跑通的前提是登录态。会员购的大部分接口要求登录Cookie最常见做法是扫码登录后把Cookie落到本地文件下次启动直接读取避免反复扫码。请求用requests.Session维持会话Session会自动管理Cookie在多个请求之间的传递。import requests import time import json import os COOKIE_FILE cookies.json def login_by_qrcode(): 扫码登录生成二维码 - 轮询扫码结果 - 保存cookie session requests.Session() # 1. 获取二维码参数 gen_url https://passport.bilibili.com/x/passport-login/web/qrcode/generate gen_resp session.get(gen_url).json() qrcode_key gen_resp[data][qrcode_key] print(请用B站手机端扫码确认登录) # 2. 轮询扫码状态间隔2秒一次最多等120秒 poll_url https://passport.bilibili.com/x/passport-login/web/qrcode/poll for _ in range(60): poll_resp session.get(poll_url, params{qrcode_key: qrcode_key}).json() if poll_resp[code] 0: # 登录成功Session已自动写入Cookie save_cookie(session) return session time.sleep(2) raise TimeoutError(扫码超时请重试) def save_cookie(session): 把Session中的Cookie序列化保存到本地 with open(COOKIE_FILE, w, encodingutf-8) as f: json.dump(session.cookies.get_dict(), f, ensure_asciiFalse, indent2) def load_cookie(session): 启动时加载Cookie避免每次都要扫码 if os.path.exists(COOKIE_FILE): with open(COOKIE_FILE, r, encodingutf-8) as f: cookies json.load(f) session.cookies.update(cookies) return True return False逻辑说明login_by_qrcode先生成二维码参数用一个for循环轮询扫码状态code 0表示手机端确认登录此时requests.Session已经把登录Cookie吸收进会话。save_cookie把Cookie字典序列化到本地load_cookie在程序启动时先尝试加载加载成功就跳过扫码步骤。参数说明扫码轮询间隔用time.sleep(2)这个频率对应二维码短轮询接口的合理调用节奏低于1秒容易触发频率限制。超时上限60 * 2 120秒是因为二维码本身有效期约两分钟。Cookie里最关键的是SESSDATA字段它决定登录态是否有效其次是buvid3这是设备指纹由首次访问页面时服务端种下。如果发现SESSDATA过期程序里应主动删除本地Cookie文件重新扫码不要带着失效Cookie继续发请求那样只会加快账号被风控标记的速度。2.3 请求头与风控前置参数很多人在练习时遇到-412或者「请求被拦截」第一反应是IP被拉黑实际排查下来大部分是请求头不齐全。会员购的服务端对几个HTTP头字段的校验比较敏感练习环境里最好固定一组常态值全局复用。HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://show.bilibili.com/, Origin: https://show.bilibili.com, Accept: application/json, text/plain, */*, Content-Type: application/json, }逻辑说明Referer和Origin是服务端判断请求来源的关键头纯接口脚本如果缺了这两个头风控系统会直接把请求归类为非浏览器来源。User-Agent要和真实浏览器的版本格式一致并且全局只固定一个版本轮换UA反而更可疑。参数说明Accept声明希望返回JSON格式Content-Type: application/json只在POST且body为JSON时是必填。会员购接口还会校验buvid3这个Cookie值不要手动伪造正确做法是脚本启动后先访问一次show.bilibili.com首页让服务端种下之后再进抢票流程。伪造buvid3的格式很容易被识别一旦识别就直接进风控名单后续所有请求都会带上 -412。3. 抢票核心循环轮询节奏、下单时序与并发控制3.1 轮询策略高频探测与库存判定纯接口抢票的代码骨架和秒杀脚本是同一套一个while循环不断请求活动详情接口解析库存字段库存大于0就转入下单流程。关键不是循环写得有多快而是每次探测之间保持可控间隔。间隔太密会触发风控太疏又会错过放票瞬间的库存释放这是一个需要慢慢校准的平衡点。def poll_stock(activity_id, sku_id, interval0.8, max_wait600): 轮询库存直到有票或超时 start time.time() while time.time() - start max_wait: detail session.get( https://show.bilibili.com/api/ticket/activity/detail, params{id: activity_id} ).json() # 从返回中找到目标sku的库存字段 sku_list detail.get(data, {}).get(skus, []) for sku in sku_list: if sku[id] sku_id and int(sku.get(stock, 0)) 0: return sku # 有票返回sku快照 # 随机化间隔避免请求时间完全固定 time.sleep(interval * random.uniform(0.8, 1.2)) return None # 超时未检测到票逻辑说明每次请求活动详情接口获取最新票档数据遍历skus数组找目标sku_id它的stock大于0就立即返回。这里返回的是sku快照而不是直接下单目的是把「探测」和「下单」拆开中间可以插入验证码预演、日志记录等逻辑也方便出问题时单独排查。参数说明interval0.8是基准轮询间隔放票前30秒可以调到0.5秒超过30秒还没票就退回1秒以上避免无意义高频请求。max_wait600是最大等待时间防止活动取消或接口异常时程序死循环。time.sleep(interval * random.uniform(0.8, 1.2))是给每次轮询加随机抖动固定间隔在服务端日志里是非常明显的机器特征。库存字段在真实接口里不一定叫stock可能是stock_count、remain_num或者quota抓包确认字段名后再写死。3.2 下单时序预填信息与秒级提交从探测到库存到订单创建成功之间通常还隔着「确认订单」的信息校验。脚本要做的不是抢在所有人前面点按钮而是把下单前的前置条件提前做完。实名购票人、收货地址、观影人信息这些配置属于账号维度的数据提前在账户里配好下单接口才会秒级通过。这些前置信息如果缺失下单接口会返回具体的错误码但等你看到错误码再回头去配置票早就没了。def create_order(item_id, sku_id, num1): 创建订单传入场次id、票档id和购买数量 order_payload { item_id: item_id, # 场次id来自活动详情接口 sku_id: sku_id, # 票档id和轮询时用的是同一个值 num: num, # 购买数量注意票档限购规则 } resp session.post( https://show.bilibili.com/api/ticket/order/create, jsonorder_payload, headersHEADERS, timeout3 ) result resp.json() if result[code] 0: return result[data][order_id] # 非0返回打印完整响应方便和抓包结果对比 print(f[下单失败] code{result[code]}, msg{result.get(message)}) return None逻辑说明下单接口的请求体字段相对固定但item_id和sku_id必须和库存探测时用的是同一批数据否则会出现「探测时有票、下单报活动不存在」这类错。timeout3很关键放票高峰期下单接口响应普遍变慢超时设太长会让线程一直挂着挤占后续重试的机会。参数说明num是购买数量漫展票档一般有限购规则超限会被直接拒绝。sku_id特别注意一点活动详情接口返回的skus数组里有一个id字段页面展示的票档ID可能是另一个字段抓包时确认下单接口实际接收的是哪个这个字段选错是下单失败的常见原因。下单失败的重试间隔建议0.5秒最多重试3次不要无限重试。3.3 并发控制单账号多场次与线程锁漫展通常有多个场次或一个活动多档票单线程轮询只能盯一个票档。常见做法是给每个sku_id开一个工作线程去轮询命中后进入下单流程。多线程练习里最容易出的问题是多个线程同时探测到库存、同时去下单结果库存被第一个订单占用其余订单全部失败还占用了宝贵的请求窗口。from concurrent.futures import ThreadPoolExecutor import threading order_lock threading.Lock() def watch_and_buy(activity_id, sku_id): 单个票档的完整流程轮询到票后下单 sku poll_stock(activity_id, sku_id) if not sku: return fsku {sku_id} 无票 # 下单是写操作加锁防止多线程同时下单 with order_lock: order_id create_order(activity_id, sku_id, 1) if order_id: return f下单成功: {order_id} return fsku {sku_id} 下单失败 # 同时盯多个票档 sku_id_list [1001, 1002, 1003] with ThreadPoolExecutor(max_workers3) as executor: futures [executor.submit(watch_and_buy, activity_id, sku_id) for sku_id in sku_id_list] for f in futures: print(f.result())逻辑说明ThreadPoolExecutor同时跑多个watch_and_buy任务每个任务独立轮询一个票档。order_lock保证同一时刻只有一个线程在执行create_order避免多个线程同时下单导致请求错乱。这里不用协程的原因很简单requests是同步阻塞库协程需要换异步客户端而且抢票场景里网络IO的瓶颈远小于Python GIL的限制线程池足够。参数说明max_workers3对应同时监控的票档数量建议不要超过5。会员购服务端对单IP的并发请求上限很敏感线程开太多会让整个IP段被风控所有票档全部探测不到库存。如果需要同时盯更多场次把poll_stock的间隔上调到1.2秒以上给每个线程的请求留出时间上的错峰。4. 验证码预演练习把滑块缺口定位与轨迹模拟跑通4.1 预演练什么样本采集、识别结果与打标口径抢票过程中弹出的验证码以滑块为主。纯接口脚本要过滑块只能走「图片识别 轨迹模拟」这一条路。标题里「验证码预演」的用意是不要在人多的线上环境里临场调参而是把识别模型和轨迹模型先在本地样本集上反复跑把参数调稳了再上真实流程。预演系统需要三样输入验证码背景图、滑块图、人工标注的正确缺口位置。把接口返回的图片保存下来用脚本批量给它们打标注然后让识别函数对每张样本输出预测坐标和标注坐标做对比。这样你能在写完整抢票流程之前先回答三个问题识别准确率多少、单次识别耗时多少、轨迹生成是否稳定。import os import json from collections import defaultdict SAMPLE_DIR samples stats defaultdict(lambda: {success: 0, fail: 0}) def run_pretrain(): 跑一遍历史样本统计识别通过率 bg_dir os.path.join(SAMPLE_DIR, bg) fg_dir os.path.join(SAMPLE_DIR, fg) for bg_name in os.listdir(bg_dir): bg_path os.path.join(bg_dir, bg_name) fg_name bg_name.replace(bg, fg) fg_path os.path.join(fg_dir, fg_name) if not os.path.exists(fg_path): continue # 识别函数返回缺口x坐标 gap_x find_gap_position(bg_path, fg_path) # 同名json里存人工标注的正确坐标 label_path bg_path.replace(.jpg, .json) if not os.path.exists(label_path): continue with open(label_path, r, encodingutf-8) as f: label json.load(f)[x] if abs(gap_x - label) 5: stats[matched][success] 1 else: stats[matched][fail] 1 total stats[matched][success] stats[matched][fail] rate stats[matched][success] / total * 100 print(f通过率: {rate:.1f}% ({total} 张样本))逻辑说明run_pretrain遍历本地样本目录每张背景图对应一张滑块图和一份标注JSON。识别函数算出的缺口坐标和标注坐标做对比误差小于5像素记为成功最后统计通过率。这种离线预演的优势是可重复同一批样本反复跑改了识别参数马上看到通过率变化不需要等真实弹验证码。参数说明误差阈值5像素对应普通验证码的缺口宽度如果样本是高清大图可以放宽到8像素如果是压缩严重的模糊图先考虑把阈值收紧到3像素再观察定位偏差。样本采集时注意保存原始响应不要截图二次压缩截图会引入额外噪点干扰识别标注坐标和原始图片的对应关系也会偏移。4.2 缺口定位Canny边缘加模板匹配滑块验证码的缺口识别OpenCV方案里最稳的组合是「边缘检测 模板匹配」。背景图上缺口边缘清晰滑块图的边缘特征和缺口的轮廓互补。用Canny提取背景边缘后拿滑块模板在边缘图上做滑窗匹配匹配峰值的位置就是缺口坐标。import cv2 import numpy as np def find_gap_position(bg_path, fg_path): 返回缺口中心x坐标 bg cv2.imread(bg_path) fg cv2.imread(fg_path, cv2.IMREAD_GRAYSCALE) # 背景图转灰度并做高斯模糊消除压缩噪声 gray cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY) gray cv2.GaussianBlur(gray, (5, 5), 0) # Canny边缘检测两个阈值控制边缘敏感度 edges cv2.Canny(gray, 100, 200) # 滑块图二值化让轮廓更干净 _, fg_thresh cv2.threshold(fg, 127, 255, cv2.THRESH_BINARY) # 模板匹配返回归一化相关系数 res cv2.matchTemplate(edges, fg_thresh, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc cv2.minMaxLoc(res) # max_loc 是模板左上角坐标加上模板宽度一半得到缺口中心 return max_loc[0] fg.shape[1] // 2逻辑说明背景图先转灰度再做高斯模糊(5, 5)的核大小对压缩噪点有不错的效果。Canny的两个阈值100, 200控制边缘提取的敏感度阈值太低会把背景纹理一起提出来阈值太高则保护不住弱边缘的缺口。matchTemplate用归一化相关系数做匹配峰值越大说明匹配越可信。参数说明TM_CCOEFF_NORMED对亮度变化不敏感适合验证码这类同环境生成的图片。fg.shape[1] // 2是把模板左上角换算成缺口中心点因为滑块模板匹配返回的是左上角坐标不补偿这半个宽度会让识别结果系统性偏左。如果识别出来的坐标总比真实位置偏移固定像素优先检查是不是少了这个补偿其次再查灰度化过程中有没有缩放尺寸。模板匹配方案也不是万能钥匙。遇到缺口带阴影、背景纹理密集、或者滑块模板尺寸被压缩过的验证码需要引入尺度遍历把模板按0.9、1.0、1.1三档缩放后分别匹配取全局最大值。这个改进能把通过率从60%拉到85%左右代价是识别耗时增加约一倍在预演环境里测一下耗时再决定要不要开。4.3 轨迹模拟与通过率统计识别出缺口坐标只是第一步决定验证码能不能过的往往是滑动轨迹。服务端校验两点滑动总耗时是否在合理区间、轨迹曲线是否像人手操作。匀速直线拉动基本必挂常见做法是生成一条带加速、减速和微小停顿的轨迹序列。import random def generate_track(distance): 生成带加速段和减速段的滑动轨迹 track [] current 0 mid distance * random.uniform(0.6, 0.8) while current distance: # 分段步长模拟手指加速再减速 if current mid: step random.uniform(2, 4) else: step random.uniform(0.5, 1.5) current step if current distance: current distance track.append(int(current)) # 10%概率插入重复坐标模拟停留 if random.random() 0.1: track.append(int(current)) return track逻辑说明generate_track用分段步长模拟手指滑动过程前60%到80%距离是加速期单步移动2到4像素后半段接近目标位置单步缩小到0.5到1.5像素。按10%概率插入重复坐标点模拟人手在滑动过程中的短暂停顿。轨迹生成后还要按固定时间间隔切片转成带时间戳的坐标序列才能模拟真实触摸事件。参数说明mid的随机范围0.60.8控制加速段占比不要把加速段固定在一个值轨迹太规整同样是机器特征。步长24和0.51.5决定整体滑动耗时总步数乘以时间步长大约对应0.8到2秒这个区间接近人工拖动的真实耗时。随机停顿概率0.1不用调太高停顿太多会拉高总耗时反而可能被判定为异常。预演环节还有一件容易被忽略的事把失败样本回收进样本集。真实验证码识别失败时把当时的背景图、滑块图、预测坐标、模拟轨迹结果一起归档隔一段时间重新跑预演统计通过率升没升、哪类图片总是失败一眼就能看出来。这个习惯能省掉后期在抢票现场反复试错的血泪时间。通过率目标设为85%比较现实不要迷信100%真实线上场景里滑块验证码本身就有随机性留出容错反而让你更关注重试流程。5. 避坑排查接口变动、风控拦截与图形化界面卡死5.1 现象接口返回 -412请求被风控拦截现象轮询或下单请求返回{code: -412, message: 请求被拦截}一旦出现就是连续多条过几分钟自己恢复。这种报错在放票瞬间最容易出现因为同一时刻大量请求集中在一个IP段。原因最常见是轮询频率过高单IP短时间请求次数超过阈值其次是请求头缺Referer或buvid3系统判定为非浏览器来源。第三种是IP段本身被其他爬虫连累进了共享黑名单。解决把poll_stock的间隔从0.5秒调到1秒以上用interval * random.uniform(0.8, 1.2)做随机抖动确认HEADERS里Referer是https://show.bilibili.com/buvid3通过启动时访问首页自然种下。如果 -412 持续不退先停掉脚本等10分钟让IP冷却再检查本机有没有其他进程在跑类似的高频请求。5.2 现象库存显示有票下单却返回「活动已结束」现象轮询探测到stock 0马上调create_order结果返回活动已结束或票档不存在。这是抢票脚本最气人的场景看起来是逻辑判断错了其实是数据源不同步。原因活动详情接口有CDN缓存返回的库存快照可能滞后十几秒下单接口拿到的才是真实实时库存。另一个常见原因是下单接口需要的sku_id和详情接口返回的skus数组里的id不是同一个字段页面展示ID和下单ID经常同值不同名。解决探测到库存后不要立刻当成必然成功给下单加重试机制第一次失败间隔0.5秒再试最多3次如果反复报「票档不存在」回到抓包工具里看下单请求真实发送的body找到正确的票档字段名。日志里同时打印探测时的sku快照和下单返回值对照排查两者是不是同一套数据。5.3 现象验证码识别率低预演通过率一直上不去现象同样的识别函数线上跑就是比本地预演差通过率掉到30%以下。很多人会怀疑是识别算法不行直接换深度模型但往往根因在样本和轨迹上。原因线上验证码图片的尺寸、压缩质量和本地样本不一致另一个高频原因是轨迹生成得太规整服务端能从轨迹的步长分布判断是机器。还有一种是背景图的缺口位置不在模板匹配的预期范围内比如缺口被云朵或光效遮盖边缘检测提不出缺口轮廓。解决把线上失败的验证码原图按背景、滑块、标注坐标归档回灌进预演样本集重新跑看识别失败是集中在某类图片还是全盘拉胯。轨迹参数不能固定把mid的随机范围放宽到0.50.9偶尔增加一次异常大步长比如step6的突然加速模拟人手急躁时的快速拖动。如果某类背景图始终失败考虑对这类图片单独走深度学习缺口检测不要在一个方案上死磕。5.4 现象图形化界面启动后卡死点按钮没反应现象界面能起来但点「开始抢票」后整个窗口转圈系统提示无响应。这是图形化抢票脚本最常见的翻车现场尤其是用Tkinter这类轻量UI库的时候。原因把网络请求和while轮询循环直接写在了UI主线程里。主线程被阻塞后界面事件队列没法处理鼠标点击和重绘表现就是转圈和无响应。根源是没分清UI线程和工作线程的职责。解决轮询、下单、验证码识别全部放到工作线程UI线程只负责展示状态。PyQt5用QThread加信号把日志和状态发给主线程Tkinter用threading.Thread开子线程子线程不直接改控件而是把状态放进queue.Queue主线程用after定时器轮询队列更新控件。这个架构在练习阶段就要改对后续加参数面板、加日志窗口才不会越写越难维护。5.5 现象登录态隔一段时间失效需要重新扫码现象程序跑了一段时间所有接口突然返回-101未登录本地Cookie文件还在但请求已经不被识别。这个坑很隐蔽因为Cookie文件看起来是完整的时间戳也在有效期内。原因SESSDATA过期是其中一种但更常见的是同一账号在脚本环境里被服务端判定为非常用设备主动踢下线。频繁切换IP或者高频请求都会加速这个判定。解决在公共请求返回里统一判断code -101一旦发现立即停止抢票流程并弹窗提示重新扫码不要带着失效Cookie继续空转。本地Cookie文件每次重新登录都覆盖写避免多个设备共用一份Cookie互相顶掉。练习账号和常用账号分开被风控的账号停几天再用不要反复横跳。6. 上线前最后一公里本地验证、参数收敛与日志复盘把流程跑通只是第一阶段真正判断这个图形化脚本能不能投入实战练习要过一遍四步自检。第一步是接口连通性自检。启动脚本后先用load_cookie加载登录态再调一次用户信息接口确认Cookie有效然后再进轮询。不要跳过这个检测直接开抢否则很可能带着失效Cookie空跑半小时。第二步是验证码预演回归。把样本集按时间切分成训练集和测试集识别通过率稳定在85%以上才值得开启真实流程否则继续在样本上调参。这里的通过率不是指跑一次的结果而是连续三次预演都稳定在85%以上避免参数恰好在一批样本上过拟合。第三步是参数收敛。我的默认起点是轮询间隔0.8秒放票前30秒内可以调到0.5秒下单超时3秒失败重试3次线程数3。这些参数不是固定模板每次练习结束根据日志重新校准。校准依据很简单日志里如果出现 -412轮询间隔调大如果出现「有票但下单失败」检查票档字段和重试间隔如果下单超时频繁把timeout从3秒调到4秒但要同步减少重试次数。第四步是日志完整度。日志里至少要记录四样东西每次轮询的库存快照、下单请求的完整返回体、验证码识别的预测坐标与实际结果、每次 -412 或 -101 出现的上下文。复盘时打开日志按时间轴看哪一步慢了、哪一步被拦截了、哪一步返回了意外字段一目了然。这一步我会重点验证一件事图形界面的状态展示和真实流程是否同步。界面不能只显示「抢票中」这种笼统文字要把「轮询中 / 检测到库存 / 创建订单中 / 订单成功 / 失败原因」分阶段展示这样人才能判断脚本当前卡在哪一步决定要不要人工介入。这个练习项目做到最后我最大的教训是纯接口方案的难点从来不是某个接口写得对不对而是登录态、库存缓存、风控策略这三样看不见的东西在时刻变化。你以为调通了下单接口就结束了其实真正的工程量全在日志、参数和样本集这三样积累上。把验证码预演做扎实把每一条轮询和下单响应都记录归档比抄一份跑得快的代码有用得多。希望帮到你。本文还有配套的精品资源点击获取