☰
京东抢购脚本 rush_to_purchase:轻量级浏览器端秒杀实现
2026/10/13 7:39:20 网站建设 项目流程

简介:这是一份面向普通消费者的京东网页端抢购辅助工具,以抢购茅台等热门商品为典型场景,专为缺乏编程基础但希望提升抢购成功率的用户设计。资源以Chrome插件形式实现,兼容Chrome及360等Chromium内核浏览器,无需安装Python环境,显著降低使用门槛。压缩包共8个文件,含核心逻辑脚本(index.js)、jQuery依赖库、插件配置清单(manifest.json)、前端界面(index.html)、图标资源(icon.png)及说明文档(README.md)、许可证与开发规范文件,整体仅60KB,轻量易部署。目前已有6192人学习下载,体现了较强的实际应用热度。读者可直接加载插件调试运行,完整掌握从页面监听、倒计时触发到自动点击的全流程实现思路,并参考作者记录的开发心路与浏览器自动化限制分析,获得兼顾实用性与可理解性的轻量级抢购方案。

1. rush_to_purchase:一个轻量级京东抢购脚本,专为高并发秒杀场景设计,适合无前端工程经验但熟悉基础 JavaScript 的开发者快速上手

你有没有试过在京东抢飞天茅台?凌晨蹲点、手动刷新、验证码识别失败、提交瞬间“库存已空”——这不是玄学,是典型客户端侧资源竞争下的时序失控。rush_to_purchase 不是一个黑盒 GUI 工具,也不是封装了 Selenium 的重型爬虫框架;它是一份仅 300 行以内的纯浏览器端 JavaScript 脚本,运行在京东商品详情页控制台中,通过精准劫持页面原生的buyNow行为、预加载 SKU 接口、绕过部分前端校验逻辑,在毫秒级窗口内完成“点击→请求→解析→提交”闭环。它不依赖 Node.js 环境,不调用任何第三方 API 服务,不注入远程代码,所有逻辑都在用户本地浏览器沙箱内执行。适合某高校课程设计中“电商系统高并发交互模拟”模块的实操环节,也适合作为某跨平台系统中“前端抗压行为建模”的最小可验证原型(MVP)。如果你只需要一个能跑通、能调参、能看懂每行作用的抢购逻辑载体,而不是一套需要部署 Redis 队列和风控绕过的分布式抢购中台,那它就是目前最贴近“开箱即用”定义的轻量实现。


2. 核心机制拆解:为什么它能在不登录态/弱登录态下完成关键请求?

2.1 抢购流程的三阶段模型:从“人肉操作”到“脚本接管”

京东商品页抢购本质是三个强时序依赖阶段的串联:
①SKU 准备阶段:获取当前商品所有可售规格(如 500ml/1L/礼盒装)、对应 stockId、priceId、skuId;
②库存探测阶段:高频轮询/ware/dynamic/stock接口,判断目标 skuId 是否返回{"stock":1}或{"stock":2}(注意:京东返回 stock=0 不代表真无货,常为限流兜底值);
③下单提交阶段:构造符合京东风控签名规则的buyNow请求体,携带skuId、num=1、rid(时间戳+随机数)、callback(JSONP 回调名),并复用页面已加载的JSESSIONID和pt_keyCookie。

rush_to_purchase 的精妙之处在于:它不试图“模拟登录”,而是复用当前浏览器已存在的京东登录态上下文。只要你在该浏览器 Tab 中已打开京东首页并完成过一次手动登录(哪怕只是扫码授权),脚本就能自动读取document.cookie中的pt_key、pt_pin、JSESSIONID,并将其注入后续所有 XHR 请求头。这规避了 OAuth2.0 授权码交换、Token 刷新、设备指纹绑定等重型链路,把复杂度压到最低。

2.2 关键接口逆向与参数签名逻辑还原

脚本核心依赖两个未公开文档的京东内部接口:

  • SKU 查询接口:https://item.jd.com/xxx.html?_format=json(实际为页面 HTML 中<script>块内硬编码的window.pageConfig对象)
    → 脚本通过document.querySelector('script:contains("pageConfig")')提取原始 JSON 字符串,再正则匹配出skuId和stockUrl模板。

  • 库存轮询接口:https://c0.3.cn/stock?skuId=${skuId}&area=1_72_2799_0&venderId=1000073245&buyNum=1&extraParam={...}
    → 其中extraParam是 Base64 编码的 JSON,内容包含originid(固定为"1")、sceneid(固定为"800")、jda(从document.cookie提取的jda字段)。

最关键的签名字段rid并非服务端生成,而是客户端计算:

function genRid() { const t = Date.now(); const r = Math.floor(Math.random() * 1000); return `${t}${r}`; }

这个rid同时作为 URL 参数和 POST body 字段,京东后端据此做请求去重和频控。脚本中所有rid生成逻辑必须严格统一,否则会出现“请求已处理”错误。

提示:area参数格式为province_city_county_0,例如北京朝阳区为1_72_2799_0。脚本默认使用1_72_2799_0(北京市),若需切换,需手动修改config.area字段并确保与京东收货地址匹配,否则库存返回stock=0。

2.3 DOM 事件劫持:如何让“立即抢购”按钮变成脚本触发器?

脚本不主动创建新按钮,而是监听并重写页面原有按钮行为。京东商品页的“立即抢购”按钮 DOM 结构通常为:

<button class="btn-buy">!function(){if(!document.cookie.match(/pt_key=/))return console.error("❌ 未检测到京东登录态,请先手动登录");const e=document.createElement("script");e.src="https://cdn.jsdelivr.net/gh/xxx/rush_to_purchase@main/dist/rush.min.js",e.onload=()=>{console.log("✅ rush_to_purchase 加载完成,按 Ctrl+Shift+J 查看控制台日志");},document.head.appendChild(e);}();

⚠️ 实际使用时请将https://cdn.jsdelivr.net/gh/xxx/...替换为真实托管地址(如 GitHub Pages 或 Gitee Pages 链接)。该链接指向编译后的rush.min.js,体积约 12KB,含完整逻辑与日志输出。

3.3 配置项详解:修改哪些参数能真正影响抢购成功率?

脚本通过全局window.rushConfig对象暴露可配置项,可在加载后、启动前覆盖默认值:

配置项类型默认值说明
targetSkuIdstring""必填。从商品页 URL 或 DOM 中提取的 18 位数字 SKU,如"100012043978"
maxRetrynumber50库存轮询最大重试次数(每次间隔 200ms),超时后自动停止
areastring"1_72_2799_0"收货地区编码,格式province_city_county_0,错误会导致库存始终为 0
autoSubmitbooleantrue设为false时,仅探测库存并打印日志,不自动提交订单(调试推荐)
logLevel"debug" | "info" | "warn""info"控制台日志粒度,debug会打印每次请求的完整 URL 和响应体

修改方式(在脚本加载完成后、点击按钮前执行):

window.rushConfig = { targetSkuId: "100012043978", maxRetry: 100, area: "1_72_2799_0", autoSubmit: true, logLevel: "debug" };

血泪经验:maxRetry不宜设为Infinity。京东对同一 IP 的/stock接口有 QPS 限制,连续 200 次请求(约 40 秒)后可能触发403 Forbidden,此时需暂停 5 分钟再试。我一般设为80,配合200ms间隔,覆盖 16 秒黄金窗口。


4. 避坑指南:五个真实翻车现场与对应解法

4.1 现象:控制台报错TypeError: Cannot read property 'cookie' of undefined

原因:脚本尝试读取document.cookie,但当前页面非京东域名(如你在google.com控制台粘贴执行)或处于 iframe 子页面。
解决:务必在京东商品详情页(URL 包含item.jd.com/)的顶层窗口 Console 中执行。可通过console.log(location.href)验证当前 URL。

4.2 现象:库存轮询一直返回{"stock":0,"skuId":"xxx"},但页面显示“有货”

原因:area参数与京东账户默认收货地址不匹配。京东库存是区域分仓的,area=1_72_2799_0(北京朝阳)查不到上海仓的货。
解决:打开京东 App 或网页端“我的地址”,复制“默认地址”的省市区编码(如上海浦东新区为2_28_1422_0),替换rushConfig.area。注意末尾_0不可省略。

4.3 现象:点击按钮后无反应,控制台无日志输出

原因:京东前端更新了按钮 class 名或 data 属性,导致脚本的 DOM 查询选择器失效。常见于大促前页面改版。
解决:手动执行document.querySelector('.btn-buy'),若返回null,则需定位新按钮。通常新结构为<a class="btn-special">const BUTTON_SELECTOR = '.btn-special, .buy-btn, .btn-buy'; // 支持多选择器

4.4 现象:提交订单时返回{"resultCode":60012,"resultMessage":"请求非法"}

原因:rid字段未同步更新到请求 body 和 URL 参数中,或JSESSIONID过期(超过 30 分钟未操作)。
解决:检查脚本中genRid()调用是否唯一。确保每次请求都重新生成rid,且 URL 和 POST body 中的rid值完全一致。若JSESSIONID过期,手动刷新商品页即可更新 cookie。

4.5 现象:成功提交后跳转至“订单结算页”,但卡在“提交订单”按钮灰色不可点

原因:京东在结算页增加了二次风控,要求用户手动滑动验证码或短信验证,脚本无法绕过。
解决:这是京东当前最强防线,脚本不处理此环节。正确做法是将autoSubmit设为false,当脚本探测到库存为1时,立即人工介入——此时页面已加载好结算数据,你只需 2 秒内完成滑块验证并点击提交。脚本的价值在此刻体现为“提前 1.5 秒抢占结算通道”。


5. 进阶技巧:用 Chrome Performance 面板定位耗时瓶颈,把成功率从 30% 提升到 75%

5.1 为什么“快”不等于“高频”?理解浏览器事件循环与网络请求队列

很多新手以为把轮询间隔从200ms改成50ms就能提升成功率,结果反而更差。根本原因是:Chrome 对同一域名的并发 HTTP/1.1 连接数限制为 6 个。当脚本同时发起 10 个/stock请求时,后 4 个会被阻塞在 TCP 队列中,实际发出时间远晚于预期。而京东库存释放是瞬时的(毫秒级),你看到的“有货”可能是第 7 个请求才收到的响应,此时早已被他人抢走。

正确做法是:用单连接串行轮询 + 请求取消机制。rush_to_purchase 内置AbortController:

let controller = null; function pollStock() { if (controller) controller.abort(); // 取消上一轮未完成请求 controller = new AbortController(); fetch(`https://c0.3.cn/stock?skuId=${sku}&rid=${genRid()}`, { signal: controller.signal }) .then(r => r.json()) .then(data => { if (data.stock > 0) { console.log(`🎉 探测到库存:${data.stock},准备提交`); submitOrder(); } else { setTimeout(pollStock, 200); // 下一轮 } }); }

这样确保任意时刻最多 1 个/stock请求在飞,避免队列堆积,响应延迟稳定在200±10ms。

5.2 用 Performance 面板抓取真实耗时:三步锁定性能短板

当你发现脚本在某次大促中成功率骤降,不要盲目调参。打开 Chrome DevTools → Performance 标签 → 点击录制(●)→ 在商品页手动点击“立即抢购” → 等待 5 秒后停止录制 → 查看火焰图:

  • 关注Timer Fired事件:若两个pollStock调用间隔远大于200ms(如 800ms),说明主线程被其他脚本阻塞(如京东页面自身的促销动画 JS);
  • 查看Network时间轴:找到/stock请求,展开看Queueing时间。若 > 100ms,证明并发连接打满,需降低频率或启用 HTTP/2(需服务端支持,脚本侧不可控);
  • 检查Scripting时间:若单次pollStock执行耗时 > 50ms,说明genRid()或 DOM 查询逻辑有冗余(如用了querySelectorAll遍历全页)。

我曾遇到一次问题:脚本在某次改版后Scripting耗时飙升至 120ms。定位发现是document.cookie读取时,京东页面注入了 200+ 条测试用 cookie,document.cookie字符串长达 8KB,正则匹配变慢。解法是缓存pt_key值:

let cachedPtKey = null; function getPtKey() { if (cachedPtKey) return cachedPtKey; const match = document.cookie.match(/pt_key=([^;]+)/); cachedPtKey = match ? match[1] : ""; return cachedPtKey; }

优化后Scripting降至 8ms,成功率从 32% 提升至 75%。

5.3 一个反直觉但有效的技巧:故意延迟首次请求

京东风控对“页面加载后立即高频请求”的行为敏感度极高。rush_to_purchase 默认在 DOM Ready 后立刻开始轮询,但实测发现:等待 1.8~2.3 秒后再发起第一次/stock请求,被拦截概率下降 40%。原理可能是避开京东首屏资源加载高峰的流量特征指纹。

在脚本中加入:

// 在 init() 函数内 setTimeout(() => { console.log("⏱️ 休眠 2.1 秒后启动轮询"); pollStock(); }, 2100);

这个 2.1 秒不是拍脑袋——它是某次大促中,我用 Performance 面板统计京东首页DOMContentLoaded到window.load的 P95 值(2087ms),向上取整得到的。从那以后我每次上线新脚本,都强制走一遍Performance → Collect → Analyze → Set Delay流程,再不凭感觉调参。

希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询