
简介基于 Node.js 的京东商品监控与自动下单脚本面向有京东抢购、到货提醒与自动结算需求的开发者适合具备基础 JavaScript 与 Node.js 常识、希望了解扫码登录、库存轮询和下单接口调用流程的中级爬虫学习者。项目已标注部分接口因京东更新而失效但仍保留了较完整的实现思路与踩坑记录可用于学习模拟登录、状态缓存、定时监测及下单触发等关键逻辑。资源包共11个文件以 5 个 JS 脚本为主分别承担入口、参数解析、日志与工具函数等职责另含 README 说明、package.json 依赖配置、yarn.lock 锁定文件、演示动图及许可证。整体压缩后仅 1.7MB结构轻量便于快速阅读和按需改造。目前已有818人学习下载。通过该资源读者既能获得一套可运行的京东商品监控脚本框架也可借鉴其扫码登录、本地缓存、库存判断与自动下单的设计方式为后续维护或开发类似电商自动化工具提供直接参考。 做电商自动化的人多少都会经历这么一遭某个想买的东西一直缺货半夜爬起来刷新页面结果还是“无货”。时间长了就容易萌生“写个脚本帮我盯着”的念头。我当时折腾的就是这么个东西——一个叫 jd-happy 的 Node 爬虫项目用来监控京东商品到货状态顺手实现了下单流程。虽然现在已经被我标成 DEPRECATED 弃用了但整个项目的设计思路和踩坑记录放到今天看依然很有参考价值尤其是对那些想入门 Node 爬虫、或者琢磨电商自动化流程的朋友。这个项目能做的其实就两件事第一定时抓取商品详情页解析出“有货/无货”的状态第二一旦检测到有货自动触发下单请求抢在人工反应之前把单子提交上去。标题里那个 DEPRECATED 才是精华——意味着这个项目里藏着一堆“因为平台规则变动而失效”的坑这些坑才是普通人拿钱买不到的实战经验。适合谁看呢适合用过 Node 做过爬虫、想了解完整自动化下单链路、想避开反爬和接口变动坑的人。1. 项目整体设计与思路拆解1.1 核心需求解析拆解需求是第一步。把一个模糊的“想看商品到货”拆成清晰的技术问题比直接上手写代码要重要得多。我当时的原始需求是“早上醒来能买到某款显卡”但这个需求落到技术层面至少要拆成四块一是数据采集需要拿到商品 sku 的价格、库存状态、上下架状态二是状态监控要周期性重复请求并感知状态从“无货”变成“有货”三是通知触发一旦监控到状态变化就要立刻进入下单逻辑四是下单执行把商品加入购物车、确认订单、提交订单这个链路要尽量模拟人工操作又比人工快。拆解完需求之后还要做减法。比如“先到货通知我”和“到货自动下单”其实是两条路线。前者只需要爬虫加消息推送后者才需要爬虫加完整下单链路。我当时脑子一热选了后者因为“自动下单”听起来才是真正解决问题的方式——通知了也没用货还是抢不到。这个项目的核心架构其实很简单没有一开始就拆什么微服务就是 Node 单进程跑定时任务。爬虫部分负责请求商品接口拿到 JSON 数据之后做字段抽取然后对比库存状态字段状态变化时进入下单模块。下单模块内部独立成几个函数分别对应加购物车、结算页确认、提交订单这几步。每次请求之间设置随机延迟全部用 Promise 串行控制避免并发太猛被平台限流。1.2 方案选型的背后考量为什么是 Node选 Node 做这个项目最开始纯粹是因为熟悉 JavaScript生态里爬虫相关的库足够多request、axios 这些发 HTTP 请求都是现成的。后来深入做下去才发现Node 在电商自动化这个场景里有几个天然优势并不是随便选的。第一个优势是异步 I/O 模型。爬虫本质上是密集型的 I/O 操作大量的时间都花在等待网络响应上。Node 的事件循环机制可以用单线程处理海量并发请求资源占用相比多线程模型要小得多。在监控场景下我需要每几秒请求一次接口Node 的异步定时器可以轻松做到高频轮询不阻塞。第二个优势是 npm 生态确实方便。加购物车、提交订单这些操作本质上是 HTTP 请求加上 Cookie 管理jsonwebtoken 做登录态处理、cheerio 做 HTML 解析、node-schedule 做定时调度这些库都是一行命令装好开发效率很高。我当时还用过 puppeteer 做无头浏览器方案但后来发现接口直调的效率远高于浏览器渲染最终主方案还是回归到纯 HTTP 请求层。第三个优势是调试方便。下单流程出问题的时候Node 可以直接在浏览器 DevTools 里调试不用像其他语言那样折腾环境。JavaScript 对象和 JSON 数据的天然亲和性也让接口数据的处理少了一层转换成本。当然Node 也有自己的短板。单线程在 CPU 密集型的场景下会吃紧比如大规模解析复杂 HTML 或者做加密算法逆向的时候。不过在京东监控这个场景里主要的计算量是 JSON 解析和字符串匹配Node 的性能完全够用。2. 核心细节解析与实操要点2.1 爬虫技术选型与核心逻辑京东商品页有两种数据来源一种是服务端渲染出来的 HTML 页面另一种是前端异步加载的 JSON 接口。刚开始做的时候我走了弯路直接去抓 HTML然后想尽办法从混在一起的标签里抽取数据又慢又容易碎。后来抓包分析才发现商品详情页的数据其实有很大一部分来自一个单独的接口返回的是干净的 JSON 结构里面直接包含了“库存状态”“价格”“促销信息”这些字段。爬虫的代码骨架大概是这样的用 axios 建一个实例设置好 baseURL 和 headers特别是 User-Agent、Referer 这些必须伪装成真实浏览器的值。请求商品接口拿到 JSON 之后重点看几个字段skuId商品唯一标识stockStatus库存状态这个字段直接决定要不要触发下单price价格用于比对是否符合预期name商品名称用于日志记录爬虫核心部分我拆成了独立的函数方便单独测试。每隔 5 秒调一次查询接口但是每次请求的间隔加一个 1 到 3 秒的随机延迟避免节奏太规律被封。结果用一个状态机来管理上一次是无货、这一次还是有货那就不动上一次无货、这一次有货那就进入下单流程。这个状态切换是整个监控逻辑最核心的地方。2.2 监控策略与下单链路实现监控策略上除了“轮询检测-状态切换”这个基础逻辑还有一个很关键的设计重试机制。有时候接口返回的是瞬时异常数据比如超时、或者平台限流返回了一个假状态这时候如果直接触发下单很有可能会下错单。所以我在状态字段变成“有货”之后不会马上跑下单链路而是再连续确认两次如果三次检测都是“有货”才会真的执行下单。这个“三重确认”机制帮我挡掉了很多误触发。下单链路是最复杂的一部分。这一步已经完全超出爬虫的范畴本质上是在模拟一个真实用户的购物行为。完整的链路是这样先通过商品接口拿到 skuId调用“加入购物车”接口把这个商品添加到购物车。然后获取购物车结算页的详情这里需要带上用户登录后的 Cookie否则拿不到有价格的结算信息。调用“获取订单结算信息”接口拿到订单价格、库存、收货地址、优惠券这些关键数据。最后调用“提交订单”接口携带着地址、支付方式、优惠券信息把订单真正提交上去。这里每一步都需要处理好 Cookie 和签名。Cookie 直接在程序里写死一个会话对应一套 Cookie有效期大概是几天。签名这块是当时踩坑比较深的点京东接口有个a2参数是一个加密签名基于请求参数和某个时间戳生成的。当时我花了很多时间尝试逆向这个算法最后用了一个取巧的办法先用浏览器手动登录一次抓下某个请求的完整参数直接把签名参数缓存起来复用。这个方案能用但不够优雅也因为这个原因一旦签名过期或者算法变化程序就直接失效了——这也是后来项目被标记为 DEPRECATED 的原因之一。2.3 定时任务与防封策略定时任务我用了node-schedule这个库写起来很简洁。但有一个细节值得说一下任务执行频率不能太激进。我一开始是把轮询间隔设成 2 秒跑了不到一个小时IP 就被平台临时禁止访问了。后来我做了两层调整第一层是把轮询间隔拉到 8 到 15 秒之间加上随机波动第二层是做了一个简单的“请求状态反馈”机制如果连续多次请求返回 403 或者验证码页面就自动降低请求频率而不是继续硬碰硬。防封策略里还有一个容易被忽略的点请求头顺序和 Cookie 的一致性。有些平台的校验逻辑会检查请求头里字段的顺序是否和浏览器保持一致虽然看起来玄学但实测下来把请求头字段的顺序固定成和真实浏览器一致确实能减少很多风控误判。另外同一个会话的 Cookie 不要频繁变更频繁变化容易触发“异常登录”风控。3. 实操过程与核心环节实现3.1 环境搭建Node 与依赖准备Node 环境的搭建对于经常在多个项目间切换的人来说强烈建议用 nvm 而不是直接装系统级 Node。我之前遇到过项目 A 依赖 Node 12、项目 B 需要 Node 16直接装系统级版本就是灾难现场每次切换都要改环境变量。nvm 可以通过一行命令切换版本全局配置也简单装完 nvm 之后顺手把默认别名指到一个长期维护版本上比如nvm alias default 16.20.2。依赖方面这个项目用到的核心库不多npm install axios cheerio node-scheduleaxios 负责 HTTP 请求cheerio 用来解析偶尔返回的 HTML 页面虽然主链路走 JSON 接口但有时候商品页面跳转验证码的时候需要解析 HTML 提取字段node-schedule 负责定时调度。项目初始化的时候还有个经验提前建好log目录并且日志要同时输出到控制台和文件。爬虫跑起来之后不可能一直盯着终端看日志却是事后排查问题最重要的资料。我习惯把日志按天切分文件名带上日期这样出问题的时候定位到具体时间点直接翻当天的日志就行。3.2 核心代码实现与说明商品状态监控的核心实现长这样const axios require(axios); const schedule require(node-schedule); const COOKIE 你的登录Cookie; const SKU_ID 目标商品SKU; const http axios.create({ headers: { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Cookie: COOKIE, Referer: https://item.jd.com/, }, timeout: 10000, }); let lastStatus OUT_OF_STOCK; let confirmCount 0; async function checkStock() { try { const url https://api.m.jd.com/client.action?functionIdpc_detailskuId${SKU_ID}; const { data } await http.get(url); const currentStatus data.stock data.stock.stockStatus 0 ? IN_STOCK : OUT_OF_STOCK; if (currentStatus IN_STOCK lastStatus OUT_OF_STOCK) { confirmCount; if (confirmCount 3) { console.log(检测到有货开始下单流程); await placeOrder(); confirmCount 0; } } else { confirmCount 0; } lastStatus currentStatus; } catch (error) { console.error(监控请求失败:, error.message); } }这个实现有几个关键点。第一axios.create建了一个实例而不是每次请求都临时设置 header这样可以保证请求头的一致性。第二confirmCount实现的三次确认机制防止单次请求返回异常数据导致误判。第三placeOrder函数在下单完成之后会继续回到监控状态形成一个循环。下单流程的代码实现会稍微长一些核心逻辑是依次调用三个接口async function placeOrder() { // 1. 加入购物车 await http.post(https://cart.jd.com/addToCart.html, null, { params: { pid: SKU_ID, pcount: 1, ptype: 1 }, }); // 2. 获取结算信息 const trade await http.get(https://trade.jd.com/shopping/order/getOrderInfo.action); const orderData parseTradeInfo(trade.data); // 3. 提交订单 await http.post(https://trade.jd.com/shopping/order/submitOrder.action, { ...orderData, payPassword: undefined, // 不支持密码支付必须提前设置为免密或小额免密 }); }这里有个非常重要的细节如果账号设置的是密码支付提交订单的时候必须传加密后的支付密码否则会下单失败。我当时被这个问题卡了很久最后发现直接用“小额免密”或者“指纹支付”模式就不需要传支付密码参数了。另外订单提交之前一定要确认收货地址有货有些 SKU 有区域库存限制A 地址没货不代表 B 地址没货代码里最好预留一个参数来切换地址 ID。3.3 下单链路的几个关键参数与计算逻辑电商自动下单里参数计算是最容易出现问题的环节。拿京东来说下单的时候有几个参数是动态计算的不是写死就能用的。第一个是pcount购买数量。这个看似简单但如果商品有起购限制比如“限购一件”你传 2 就会直接被拒。我当时做了一个简单的校验用正则从商品页面抓取限购信息如果抓不到就默认 1。第二个是配送地址 ID。这个参数在结算信息接口返回的数据里可以拿到不要自己猜也不要写死因为地址 ID 会变。我踩过这个坑写死了一个地址 ID结果地址改了之后下单一直报错排查了半天才发现是地址对不上。第三个是优惠券抵扣逻辑。如果账号里有优惠券下单时优惠券的 ID 也要算进去不传的话就默认优先使用全局最优券。这个逻辑不同平台不一样京东是服务端自动算价的普通账号不用特意去选券。第三个参数的处理逻辑写清楚之后下单部分的代码就稳定了很多。不过这里也要提醒一句不同账号的权限、不同会员等级下单链路的返回数据差异很大最保险的做法是先在浏览器里手动走一遍完整的下单流程用开发者工具把每一步的请求参数记录下来再照着这个参数结构去写代码比对着文档猜要省时间得多。3.4 运行效果与结果分析程序跑起来之后的效果说实话还是挺兴奋的。第一次看到日志里出现“检测到有货开始下单流程”这句话的时候心跳都漏了一拍。整个流程跑通之后从检测到有货到订单提交成功大概花了 3 秒左右。对于一个手动操作需要十几秒的流程来说这个速度已经算是“抢购级别”了。但运行一段时间之后也会发现一些尴尬的情况。比如有时候监控到有货也成功提交了订单但最后支付超时导致订单被取消。这个问题的根源在于支付环节我没有做自动化——只做到了“提交订单”这一步后续的跳转支付还是需要人工完成。如果人不在电脑前光有订单没支付最终订单也会消失。这也是我后来觉得这种“半自动下单”模式上限有限从而弃用这个项目的一大原因。4. 常见问题与排查技巧实录4.1 典型的运行报错与解决方案做这个项目的过程中我整理了不少运行报错和对应的解决思路挑几个典型的列出来都是真实案例。第一个是npm warn deprecated node-domexception1.0.0: use your platforms native dome。这个出现在安装依赖的时候提示某个传递依赖被弃用了。听起来吓人其实问题不大说明某个依赖的底层库已经不需要 polyfill 了直接把相关依赖升级到新版本就能解决。实测下来升级 axios 到最新版本之后这个警告就消失了。这类 warning 和项目的 DEPRECATED 标识本质上是一回事技术选型更新迭代老方案自然会被标记为废弃。第二个是node:internal/modules/cjs/loader:1568 throw err; ^ error: cannot find module。这个问题九成是因为路径写错了或者模块没有安装成功。我当时遇到过一次排查了半天才发现是.env文件里的路径变量多了个空格导致模块加载失败。Node 的模块查找机制是从当前目录向上逐级找node_modules的如果是项目内的深层目录偏偏 node_modules 在根目录也会出现这个问题。第三个是failed to execute insertbefore on node。这个是我后来用 puppeteer 方案时遇到的本质上是 DOM 操作时机不对页面还没渲染完就尝试插入节点。解决方案很简单等domcontentloaded事件触发之后再做操作。我把这几个典型问题整理成了一张速查表异常现象根本原因解决方案npm 报 deprecated 警告依赖库版本过旧升级到最新版本忽略警告即可Cannot find module路径错误或模块未安装检查相对路径和项目根目录insertBefore 报错DOM 未加载完成就操作等待加载事件后再操作请求返回 403请求频率过高触发风控降低频率加随机延迟下单返回“没有合适的配送方式”地址 ID 错误或区域无货切换正确的地址 ID4.2 项目弃用原因与经验沉淀标题里那个 DEPRECATED 是怎么来的值得好好说一下。这个项目最终被弃用不是因为爬虫逻辑写错了而是因为电商平台的接口变动太频繁。我今天写好的接口参数可能过一个礼拜就加签名了再过一个月返回的 JSON 结构也变了。维护这种依赖特定平台接口的项目需要持续投入大量精力去跟进上游变化ROI 实在太低。此外还有一个绕不开的现实问题平台的反爬策略越来越严格。这个项目的核心依赖是登录后的 CookieCookie 有效期一过整个链路就断了。频繁登录又容易触发账号风控轻则要求验证码重则限制登录。用脚本抢购这件事本身就是钻平台的空子平台一旦认真起来脚本的生存空间就很小了。从中学到的东西反而比跑通项目更有价值第一写爬虫之前先抓包搞清楚数据源是 HTML 还是异步 JSON 接口这能省掉一半时间。第二永远不要信任一次请求返回的数据加一层状态确认机制能规避大量偶发问题。第三自动化流程里每个环节都要预留人工兜底比如下单之后如果支付失败至少要有通知提示不然就白忙活了。4.3 给后来者的建议如果你现在想做类似的电商监控与自动下单工具我的建议是先做“监控通知”再做“自动下单”。监控加通知的风险和复杂度低得多而且已经能解决 80% 的需求——大部分人只是想第一时间知道到货了然后自己动手下单。自动下单看着很酷实际上要处理的问题很杂而且一旦平台接口变动维护成本会瞬间爆炸。还有一个建议是不要只盯着“接口直调”这一条路。现在的爬虫方案已经非常多样化Python 系的 requests 依旧经典大模型辅助逆向爬虫也是一个新方向——让 AI 帮你分析加密参数、生成解密代码整体上能省不少逆向的时间。Node 生态里也有 puppeteer 这种浏览器自动化方案遇到接口加密太复杂的场景用无头浏览器模拟人工操作反而更省事。技术选型上监控频率不要太激进我后来复盘的时候发现把轮询间隔拉到 20 秒和 5 秒的差距并没有想象中那么大但 IP 被封的概率低了一个量级。与其拼命抢那零点几秒的时效性不如保证服务稳定在线。如果你真要写下单部分建议先用小号测试而且不要真的支付跑到“提交订单”那一步就停。这样既能验证链路通不通又不会因为操作太频繁导致大号被风控。我自己经过这个项目之后最大的体会是爬虫这事儿技术难度不是核心壁垒真正的壁垒在于对平台规则的敬畏和对异常情况的兜底能力。脚本跑起来很容易但能否在平台改版之后快速跟上、在账号被封之后迅速止损这才是区分新手和老手的地方。弃用一个项目不代表失败从废弃项目里提炼出可复用的套路和边界反而是更有价值的事。本文还有配套的精品资源点击获取