简介:本资源是一套面向Python开发者与自动化爱好者的技术实践脚本,聚焦于i茅台官方平台预约流程的模拟操作,旨在辅助用户理解电商抢购类自动化逻辑,适用于学习网络请求、登录鉴权、定时任务及基础反爬策略等实战场景。压缩包共10个文件,含6个核心Python脚本(如main.py主流程、login.py登录模块、encrypt.py加密处理)、2个配置与说明文本、1个Markdown文档和1个示例配置文件,整体仅8KB,轻量易读,结构清晰便于调试与二次开发。目前已有135人下载学习,适合具备基础Python和HTTP协议认知的中级学习者参考。读者可直接复现预约逻辑框架,掌握从账号登录、接口分析到定时触发的关键步骤,并通过config.py.example快速适配环境;README.md提供基础使用指引,test目录下预留验证模块,有助于理解脚本分层设计与日志追踪机制。 先交代一下背景。用过i茅台的人都知道,每天上午九点到十点那一小时有多紧张——打开App、刷新门店库存、选商品、填预约、提交,整套动作必须在几分钟内一气呵成,手一抖或者网络一卡,当天就没什么机会了。我一开始也是手动操作,坚持了半个多月,真正体会到什么叫“陪跑”。后来有一个做开发的朋友给我提了个醒:这种固定时间、固定流程的操作,本来就是自动化脚本最典型的应用场景。于是就有了这个“i茅台预约脚本.zip”项目。
这个脚本解决的事情其实很朴素:自动登录、到点自动提交预约申请、预约结果推送通知,全程不需要我盯着手机。对于每天都要抢、又不想被流程绑死的用户来说,它确实能省下不少事。而对技术爱好者来说,这个项目反过来也是练手的好素材——定时任务调度、HTTP请求模拟、登录态保持、并发控制、异常重试,这些知识点在任何一个自动化项目里都用得上。所以这篇文章不仅是讲这个zip里有什么,更多的是把我踩过的坑、验证过的方案、优化过的细节一并梳理出来,给想自己折腾一套预约脚本的人当个参考。
我先把整个项目拆开,从需求分析到代码实现再到部署排障,一条线讲清楚。
1. 项目背景与核心需求拆解
1.1 i茅台申购机制与“预约难”的根源
i茅台的申购规则看着不复杂,但细节上全是门槛。每天9:00到10:00开放预约窗口,每个实名账号一个自然日只能提交一次,提交时要选择具体的商品和门店,预约结果一般第二天公布。问题在于,热门商品尤其是飞天茅台,每次放量就那么一点,全国几十万人在同一时间抢,服务器压力大,页面点击响应慢,等你手动把商品选好、门店找到,可能系统已经提示“暂无可预约数量”。
换句话说,这个场景的痛点不是“不知道什么时候抢”,而是“知道时间但手速和网络拼不过别人”。手动操作再快,也得经历“打开App -> 进入申购页 -> 刷新列表 -> 选择门店 -> 确认提交”这五六个步骤,每步都有网络延迟和页面加载时间。脚本的价值,就是把这几步编译成一个串联请求,在整点前提前把前置状态准备好,整点那一瞬直接提交,把手工操作需要的一两分钟压缩到几秒。
1.2 脚本要解决的核心问题清单
我在动手做这个项目之前,先列了一份需求清单,把预约场景里的关键问题全部摊开。核心问题主要有这么几类:
- 定时执行:9点整必须发起请求,不能早也不能晚,早了系统不开放,晚了额度被抢光。这需要精确到秒级的调度能力。
- 登录态保持:i茅台的接口依赖登录后的身份凭证,脚本不能每次运行都重新登录,那样既慢又容易被风控。必须把登录态持久化保存。
- 门店与商品选择:预约必须指定门店和商品,不同用户的常用门店不一样,配置要灵活可改,不能写死在代码里。
- 提交后的结果确认:请求提交成功不等于预约成功,需要解析接口返回的状态码,判断是“已受理”还是“库存不足”或“重复预约”。
- 异常补偿:网络超时、接口返回异常、登录过期,这些情况必须要有重试或告警机制,不能静默失败。
这份清单基本确定了脚本的功能边界:不是去“破解”茅台系统,也不是提高中签概率,而是把“准时提交预约请求”这件事做到最大限度稳定和自动。
2. 技术选型与整体架构设计
2.1 为什么选Python而不是Shell或Node.js
项目开发前,我在技术选型上犹豫过一阵。最先排除的是纯Shell脚本。虽然系统热词里提到“shell脚本for循环”“shell脚本入门”,但Shell擅长的是文本处理和系统命令编排,处理HTTP请求、JSON解析、定时调度这些活虽然都能干,但代码写出来又长又难维护,尤其是在登录态管理和异常处理上,Shell的表达能力明显不够用。
Node.js也是备选方案之一,但我在Windows环境下测试时遇到了不少环境问题。热搜词里有一条很典型:“无法将‘npm’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”,这其实就是Node环境变量没配好或者安装不完整导致的。Node做自动化也有生态优势,但考虑到不少用户是Windows小白,让他们先装Node再配npm环境,学习成本偏高。
最后选了Python,理由很实在:requests库封装HTTP请求足够简洁,APScheduler做定时任务调度是现成的轮子,配置管理用YAML文件,普通人修改起来也没有心理负担。最关键是Python的安装和排障资料多,遇到问题搜索一下基本都有答案,这对一个要分享给别人的脚本项目来说非常重要。
2.2 脚本整体架构与模块划分
整个脚本按职责拆成了五个模块,避免把逻辑全堆在一个文件里。拆模块的好处是后期维护的时候不用通读全部代码,改哪个部分就进哪个文件。
| 模块 | 职责 | 对应文件 |
|---|---|---|
| 入口模块 | 解析配置、初始化日志、启动调度器 | main.py |
| 配置模块 | 读取账号信息、预约参数、通知参数 | config.yaml |
| 登录模块 | 处理登录逻辑、保存和恢复会话状态 | login.py |
| 预约模块 | 封装预约请求、解析响应、执行重试策略 | reserve.py |
| 通知模块 | 预约结果推送(企业微信/Server酱/邮件) | notify.py |
模块之间单向依赖:main.py只负责启动调度,到点后调用reserve.py的预约函数,reserve.py内部调用login.py提供的会话对象,预约结果通过notify.py发出来。这样设计的好处是,即使某一天通知方式要换,也只需要改notify.py,其他模块完全不用动。
2.3 目录结构与文件说明
zip包解压后的目录结构是这个样子的:
iMoutai-Reserve/ ├── main.py # 入口脚本 ├── login.py # 登录与会话管理 ├── reserve.py # 预约核心逻辑 ├── notify.py # 通知模块 ├── config.yaml # 用户配置文件 ├── requirements.txt # 依赖库清单 ├── README.md # 使用说明 └── logs/ # 运行日志目录(自建)特意把配置文件config.yaml放在根目录而不是代码里,就是考虑到用户拿到后第一件事是改配置,而不是翻代码找参数。requirements.txt里面固定了核心依赖的版本号:
requests==2.31.0 APScheduler==3.10.4 PyYAML==6.0.1版本号锁死,避免过一段时间库升级后接口变动导致脚本跑不起来。
3. 核心功能实现与实操要点
3.1 登录态与会话保持的实现细节
登录是预约的前提。i茅台的接口调用需要携带登录后的token信息,所以脚本里单独写了一个login.py,负责两件事:登录后把凭证保存到本地文件,下次运行时优先加载本地凭证,只有凭证失效时才重新走登录流程。
核心代码是这样组织的:
import json import requests from pathlib import Path class SessionManager: def __init__(self, session_file="session.json"): self.session = requests.Session() self.session.headers.update({ "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X)", "Accept": "application/json", "Content-Type": "application/json;charset=UTF-8", }) self.session_file = Path(session_file) def load_or_login(self, username, password): if self.session_file.exists(): data = json.loads(self.session_file.read_text(encoding="utf-8")) self.session.headers.update({"Authorization": data.get("token", "")}) # 这里可以做一次轻量请求验证token是否有效 if self._check_token_valid(): return True # token失效或不存在,走登录流程 return self._login(username, password) def _login(self, username, password): # 登录接口需要按实际接口调整,这里是示意 resp = self.session.post("https://api.example.com/login", json={ "username": username, "password": password, }) if resp.status_code == 200: data = resp.json() self.session.headers.update({"Authorization": data["token"]}) self.session_file.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8") return True return False这里有个容易被忽略的细节:保存会话凭证的文件,权限要设严一点。尤其Windows电脑多人共用的时候,session.json里存的是账号相关凭证,泄露了等于把账号交到别人手里。我自己的习惯是在.gitignore里把session.json和config.yaml都加进去,避免哪天把代码托管到Git仓库时把敏感信息一起传上去。
3.2 定时任务调度与准点提交策略
预约最讲究时机,调度器是整条链路的指挥官。我用的是APScheduler的CronTrigger,好处是可以精确控制秒级触发,而不是像Windows计划任务那样只能精确到分钟。
from apscheduler.schedulers.blocking import BlockingScheduler from datetime import datetime def job_reserve(): # 实际调度任务 pass scheduler = BlockingScheduler() scheduler.add_job( job_reserve, trigger="cron", hour=9, minute=0, second=0, misfire_grace_time=5, coalesce=False, )Misfire参数是个容易忽视的坑。默认情况下,如果任务因为某种原因没在指定时间执行,APScheduler会在后续补跑,这个补跑动作叫misfire。预约场景里,9点之后补跑基本没有意义,所以必须把misfire_grace_time设小一点,并且明确coalesce=False,让错过的任务直接跳过,而不是挤在一起执行。
另一个我在实测后加入的优化是“预热”。9点整直接提交请求,如果脚本是冷启动的——也就是前一次运行很久,会话可能已经过期,又或者需要重新加载配置——往往来不及。所以我加了一个预热逻辑:在8:59:50提前启动预约任务,先把会话验证、门店列表加载、商品余量确认这些前置操作跑完,然后在9:00:00那一瞬只执行“提交预约”这一个动作。这个优化对预约成功率的影响非常明显。
3.3 多账号支持与防风控设计
如果一个账号的中签率不高,自然有人想用多个账号提高机会。脚本设计时预留了多账号的扩展口,config.yaml里用一个列表来管理账号:
accounts: - username: "13800138000" password: "your_password" store_code: "GZ001" product_code: "P001" - username: "13900139000" password: "another_password" store_code: "GZ002" product_code: "P001"多账号的执行策略我选择的是串行而不是并发。原因很简单:同一时间、同一IP、多个账号同时发起登录请求,在风控系统眼里是一个极其明显的异常信号,很容易被判定为脚本批量操作。串行执行,每个账号之间随机间隔3到8秒,从行为特征上更接近人工操作。
代码里用一个简单的循环就实现了:
import time import random for account in config["accounts"]: try: reserve_for_account(account) except Exception as exc: logger.error(f"账号 {account['username']} 预约失败: {exc}") time.sleep(random.uniform(3, 8))3.4 通知模块的选型与配置
预约结果怎么通知?脚本运行在后台,用户不可能一直盯着控制台。我最早用的是邮件通知,但邮件延迟不稳定,高峰期甚至有十几分钟的延迟。后来换成了企业微信机器人,通过Webhook把结果直接推到群里或者个人微信上,基本是秒达。
企业微信机器人的通知请求非常简单:
import requests def send_wechat_message(webhook_url, content): resp = requests.post(webhook_url, json={"msgtype": "text", "text": {"content": content}}) return resp.status_code == 200配置里只需要填一个Webhook地址:
notify: wechat_webhook: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY"通知内容不要只写“预约成功”四个字,我建议把账号、门店、商品、提交时间、返回状态码都带上。哪怕预约没成功,看到原始返回信息也能快速判断是网络问题、库存问题还是账号问题。
4. 环境准备与部署运行
4.1 解压校验与zip文件常见问题
用户拿到的资源是一个zip压缩包,这看起来是个小事,但实际操作中翻车的人不在少数。热搜词里那些“file is not a zip file”“invalid zip archive: could not find eocd”“z01怎么和zip一起解压”之类的问题,我都遇到过。
先说“file is not a zip file”,绝大多数情况是这个文件下载不完整,文件头损坏。zip文件开头应该有PK标识,如果你用记事本打开一个zip文件,发现开头不是PK这两个字节,基本可以确定文件损坏。解决办法只有一个:重新下载,下载过程中确保网络稳定,不要中断。
再讲“could not find eocd”。EOCD是zip格式的结尾记录,相当于zip文件的“目录页”。缺少EOCD意味着文件尾部不完整,常见于用一些不靠谱的下载工具下载到一半就自动拼接的情况。用7-Zip可以尝试“修复压缩文件”功能,但成功率不高,最稳妥的还是重新获取完整文件。另外,如果下载到的是分卷压缩包,比如.z01加.zip这类多文件组合,需要把同一个压缩任务的所有分卷放在同一个目录,用7-Zip打开第一个.zip文件就能自动关联分卷解压,单独解压任何一部分都会报错。
4.2 Python环境配置与依赖安装
解压完成后的第一件事是装Python环境。官网下载安装包的时候要注意勾选“Add Python to PATH”,不勾的话后面在命令行敲python大概率会提示“python不是内部或外部命令”。这个报错和热搜词里“无法将‘npm’项识别为 cmdlet”的本质是一样的,都是环境变量PATH里没有程序路径,系统的解析器找不到可执行程序。
装好Python之后,建议建一个虚拟环境,不要直接把依赖装到全局。虚拟环境的好处是每个项目依赖隔离,不会互相打架。
在项目目录下执行:
python -m venv venvWindows环境激活虚拟环境:
venv\Scripts\activateLinux或macOS环境激活:
source venv/bin/activate然后安装依赖:
pip install -r requirements.txt国内用户如果pip下载速度慢,可以临时换源:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple4.3 参数配置与首次运行验证
首次运行之前,必须把config.yaml里的参数填对。重点检查三项:账号信息、门店编码、商品编码。门店编码和商品编码从哪里来?最简单的办法是自己在手机上打开i茅台App,进入预约页面,用抓包工具抓一次正常点击的请求,看请求体里传的store_code和product_code是什么值。这个步骤没法绕开,因为不同地区、不同时间点的可用门店列表是动态的,脚本不会自动去拉取全部门店信息。
填好配置后,先不要等9点整,我通常建议用“试跑模式”验证配置正确性。脚本里加了一个--dry-run参数,运行后只做登录验证、门店信息获取、参数组装,但不会真正提交预约请求。
python main.py --dry-run如果试跑日志里能看到门店名称、商品名称、账号昵称这些信息,说明配置基本没问题,可以放心等定时任务了。
4.4 开机自启与无人值守运行
预约时间是早上9点,这就意味着电脑必须在9点前是开机状态。如果用的是台式机或者公司电脑,不可能为了一个预约一直开机。我的方案是Windows任务计划程序加电源唤醒。在BIOS里设置定时唤醒比较麻烦,但Windows任务计划可以配合“唤醒计算机来运行此任务”这个选项,前提是主板支持并开启了相应的电源管理功能。
Linux服务器或者树莓派这类设备就没有这个烦恼,一条crontab就搞定:
0 8 59 * * * cd /path/to/iMoutai-Reserve && /usr/bin/python3 main.py --dry-run >> logs/cron.log 2>&1注意crontab里我写的是8点59分跑dry-run,因为脚本内部会自动等到9点整执行提交逻辑,外部只需要保证提前一点把脚本唤醒。
5. 常见问题与排查技巧实录
5.1 解压与文件校验类问题速查
我把实际操作中常见的解压问题整理成了一张表,按优先级排查。
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 解压提示“file is not a zip file” | 文件下载不完整或格式错误 | 重新下载,确认文件大小与源文件一致 |
| 提示“could not find eocd” | 文件尾部缺失 | 优先尝试7-Zip修复,不行就重新下载 |
| 提示“error opening zip file or jar manifest missing” | 用Java的jar命令打开了非jar文件 | 确认文件扩展名,不要用jar命令解压zip |
| 分卷文件.z01和.zip无法解压 | 分卷不完整或不在同一目录 | 放同一目录,用7-Zip打开.zip文件 |
| 解压后运行报编码错误 | 源码文件编码与系统不一致 | 用UTF-8编码保存,Windows下不要用记事本编辑py文件 |
5.2 环境与命令识别类问题
这类问题在热搜词里出现的频率最高,核心就是环境变量。Windows下有两个命令经常被人问:一个是python不是内部或外部命令,另一个是npm无法识别。两个问题的根源一模一样,都是软件安装时没有把可执行文件目录注册到PATH环境变量。
解法有两个:要么重装软件并勾选“Add to PATH”,要么手动把程序安装路径加到系统环境变量里。手动添加的路径要看清楚,必须以可执行文件所在目录为准,比如npm在C:\Program Files\nodejs\,那就把这个目录加进去,而不是把nodejs安装总目录加进去。
另外有一个和PowerShell相关的坑:在Windows上运行脚本时提示“因为在此系统上禁止运行脚本”,这是PowerShell的执行策略限制。想临时绕过可以用:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass这种方式只对当前窗口生效,不改变系统全局配置,安全一些。永久修改执行策略不建议,因为会降低系统安全性。
5.3 预约失败类问题与日志分析
预约过程中最大的不确定性来自接口返回的业务状态码。我用表格整理了一些常见返回情况和处理建议。
| 返回现象 | 可能原因 | 处理建议 |
|---|---|---|
| 登录过期或token失效 | 会话凭证保存时间过长 | 重新登录,检查login.py里的凭证刷新逻辑 |
| 返回“操作频繁” | 请求频率过高触发风控 | 增加账号间延迟,检查是否有并发请求 |
| 提示“无可用库存” | 门店库存已空或还未放量 | 确认门店编码正确,提前验证门店可用性 |
| 提交成功但无中签记录 | 预约受理≠中签 | 正常现象,中签靠随机,脚本只保证提交 |
遇到失败不要急着改代码,先看日志。脚本里我加了详细的结构化日志,每条日志都带时间戳、模块名、请求URL和响应摘要。
5.4 排查方法论:从日志定位到修复
这里分享一个我自己的排查套路。第一件事永远是看日志,日志里没有的信息,再去翻代码。脚本启动时会打印当前用的配置文件路径、登录状态、定时任务注册情况,这些信息能帮你快速判断问题在哪一层。第二件事是抓包对比。脚本提交请求和App提交请求,返回结果不一致时,用抓包工具对比一下请求头差异,往往是缺少某个请求头或者请求体字段导致被拒绝。第三件事才是改代码。每一次改动保持单一变量原则,同一个时间只改一个参数,改完立刻跑dry-run验证,不要同时改三个地方然后猜是哪一处生效了。
6. 合规边界与使用建议
6.1 脚本的风险边界
先把这个脚本的边界说清楚。它做的是把用户在App上手动点击的操作,转换成自动化请求来执行,本质仍然是用户主动提交预约,没有去篡改接口数据,也没有绕开平台的身份验证机制。从技术性质上讲,这和自动抢票类脚本属于同一类别,都有被平台风控识别和限制的可能性。
这里要特别提醒一下:不要把这个脚本用于任何商业代抢服务,不要收取费用帮别人抢购,更不要用脚本去囤积大量账号批量操作。这类行为已经超出个人自动化的范畴,一旦被认定为恶意操作,账号封禁都是轻的,还有可能承担相关法律责任。我自己做这个脚本的定位就是个人学习自动化技术和自用体验,仅此而已。
6.2 降低使用风险的操作建议
从实操层面,我总结了几个降低风险的注意事项。第一,控制请求频率。不管配置了多少账号,请求间隔不要短于3秒,不要用并发模式,行为越接近真人越安全。第二,不要全天高频请求。脚本只在预约时间点运行一次,不要在非预约时段反复请求接口做测试,大量的探测请求更容易触发风控。第三,用独立的账号测试。调试阶段不要用自己常用、有历史数据的账号,新建一个小号跑流程,等稳定了再切换到正式账号。第四,关注脚本更新。平台接口如果调整了参数结构,脚本需要同步更新,长期不维护的脚本成功率会越来越低。
使用脚本还需要平常心。脚本帮我们把“准时提交”这个动作做到极致,但最终中签结果仍然取决于平台方的抽选规则,不是提交快就一定能中。放平心态,把它当做一个例行任务来看,中了是惊喜,没中也别太纠结。
最后补充一点实操心得
项目做完之后我发现,这个脚本最有价值的不是“提高中签率”——说实话,中签率这东西脚本帮不上什么忙——而是它让我对“定时任务+HTTP请求+配置驱动”这套自动化模式有了完整的实践经验。后来我把这套架构稍微改动,复用到其他几个场景:抢菜、抢课、定时打卡,思路都是相通的。
如果你也准备上手这个脚本,我的建议是不要急着等9点整,先把dry-run跑通,确认登录、配置、通知全部正常再上正式任务。最后再分享一个小技巧:在config.yaml里给每个账号加一个备注字段,预约结果通知时把备注也带上,月底回看通知记录,你能直观看出哪个门店、哪个商品的成功率更高,这对后续调整预约策略很有帮助。
本文还有配套的精品资源,点击获取