
简介这是一份面向Node.js初学者的电商自动化爬虫示例基于京东网页端实现指定商品到货监控与自动下单。项目已因京东接口更新而标注废弃但代码中的扫码登录、地区库存查询、抢购触发、本地登录状态缓存等流程仍可作为爬虫与网页自动化学习的参考目录中的演示动图也能直观展示运行效果。压缩包共11个文件以JavaScript脚本为主体辅以Markdown说明、JSON参数配置、License和GIF演示等内容整体仅1.7MB体积小巧、结构紧凑便于快速下载和定向研读。目前已有820人学习下载。读者可从中掌握Node脚本如何组织请求参数、解析页面数据、复用登录状态以及设计简易定时监控逻辑的方法并可将相似思路迁移到其他电商或预约场景适合想动手构建小型自动化工具的技术爱好者。1. 一个盯着到货的爬虫为什么会走到下单这一步如果你抢过限量的数码产品大概率经历过这种事商品页刷了几百遍一直显示到货通知某天凌晨三点突然刷出加入购物车点进去提交订单却发现地址栏被清空、库存只剩个位数。某开发者在 GitHub 上开源过一个叫 jd-happy 的 Node 爬虫项目它的核心思路很简单用脚本代替人眼循环请求商品详情接口一旦库存状态从无货变成有货就立刻触发下单流程。项目本身已经标记为 DEPRECATED(不再维护)但里面对商品监控、登录态维护、下单接口的封装方式到现在依然是写电商抢购脚本、库存监控机器人时可以借鉴的样本。这个项目适合两类人一类是刚学 Node.js、想看看真实爬虫项目长什么样的新手另一类是手里有类似需求、需要快速实现监控商品 自动下单的开发者。后面我会把它拆开讲清楚——模块怎么组织、登录态怎么处理、下单链路怎么打通以及最后怎么在它的基础上改造成你自己的工具。2. 项目骨架与模块职责一个典型的 Node 定时任务 HTTP 客户端结构2.1 目录结构与核心文件职责先把项目的整体结构摆出来。这个项目不是一个大型框架而是典型的脚本聚合型结构所有模块围绕监控 下单这条主线展开jd-happy/ ├── config/ │ ├── index.js # 全局配置商品 ID、购买数量、请求间隔 │ └── goods.json # 待监控商品列表支持多商品 ├── lib/ │ ├── http.js # 封装 axios/fetch 请求带重试和超时 │ ├── login.js # 二维码登录态获取与 cookie 管理 │ ├── monitor.js # 轮询商品状态判断是否可下单 │ ├── order.js # 下单流程加入购物车 - 结算 - 提交订单 │ └── utils.js # 时间格式化、随机延时、日志输出 ├── logs/ # 运行日志按天分割 ├── app.js # 入口文件启动监控循环 └── package.json # 依赖声明axios, qrcode-terminal, moment 等重点看 lib 目录下的四个模块。http.js 是所有请求的基础通道它把公共逻辑——设置 User-Agent、Cookie 传递、请求超时、失败重试——集中在这一层业务模块里只需要关心接口路径和参数。login.js 负责登录态monitor.js 是大脑order.js 是最终执行者。2.2 核心链路从轮询商品到提交订单的完整时序这个项目的本质是一个有限状态机。每次循环都执行四次判断检查登录态是否有效(失效则要求重新扫码)请求商品详情接口解析库存字段若库存状态为可下单调用加购接口加购成功后走结算页接口最后提交订单关键点在于第四步设计得比较保守。很多类似的脚本会把加购和下单写在一起但 jd-happy 把订单提交做成独立函数并且在下单前会做一次页面跳转校验。这是因为电商平台的下单接口通常依赖会话中的地址 ID 和订单 token直接从脚本侧拼请求容易触发风控而模拟从购物车进入结算页这一步能最大限度复用正常用户的操作路径。2.3 技术选型为什么是 Node 而不是 Python选择 Node.js 跑这个项目有几个现实原因一是异步 I/O 适合高频轮询场景单线程事件循环可以同时监控多个商品而不需要额外开线程池二是 axios 的拦截器机制让 cookie 管理与重试逻辑变得非常干净三是 qrcode-terminal 这个库能直接在终端里渲染二维码省去了自己写扫码登录交互界面。如果换成 Pythonrequests 库也能实现但终端二维码这一块通常得额外装第三方工具Node 生态里直接就有。3. 核心模块拆解从登录到下单的完整链路实现3.1 登录模块二维码登录与 cookie 持久化登录是所有电商脚本的第一道坎。jd-happy 采用的方案是二维码登录实现思路分三步走。第一步调用平台生成二维码 key 的接口拿到一个唯一标识第二步把 key 传给 qrcode-terminal 在终端渲染成二维码用户用 App 扫码确认第三步循环轮询登录态接口扫码成功后服务端返回 cookie。// lib/login.js const axios require(axios); const qrcode require(qrcode-terminal); async function getQRCode() { const resp await axios.get(https://passport.example.com/qrcode/generate, { headers: { User-Agent: Mozilla/5.0 } }); const { qrId, code } resp.data; // 在终端绘制二维码qrId 是后续轮询登录态的唯一凭证 qrcode.generate(decodeURIComponent(code), { small: true }); return qrId; } async function waitForScan(qrId) { // 轮询登录结果返回完整 cookie 字符串 return new Promise((resolve, reject) { const timer setInterval(async () { const resp await axios.get( https://passport.example.com/qrcode/check, { params: { qrId } } ); // 响应中的 code 字段标识扫码状态等待中/已扫未确认/成功 if (resp.data.code success) { clearInterval(timer); resolve(resp.data.cookie); } }, 2000); }); }这段代码里有两个值得留意的设计。其一二维码 key 的生成和登录态检查是两个完全独立的接口中间靠 qrId 关联这个模式几乎是所有扫码登录的通用套路其二轮询间隔固定为 2 秒不宜太短否则会被风控识别。cookie 拿到后项目会把它持久化到本地文件下次启动时直接读取避免每次都要重新扫码。3.2 监控模块解析库存状态与防抖控制监控模块是整个项目的心脏。它要解决的不只是能不能请求到数据而是怎么判断可以下单。不同平台的库存状态字段名完全不一样有的是 status有的是 stock有的藏在 sku 列表里。jd-happy 的做法是先获取完整的商品详情 JSON再递归寻找包含stock或status关键字的字段。// lib/monitor.js const { sleep } require(./utils); async function checkStock(goodsId, skuId, cookie) { const url https://item.example.com/${goodsId}.html; const resp await http.get(url, { headers: { Cookie: cookie } }); // 解析嵌套 JSON 中的库存字段不同 SKU 结果可能不同 const stockInfo extractStock(resp.data); const isAvailable stockInfo.stock 1 stockInfo.sellable true; if (isAvailable) { logger.info([到货] goodsId${goodsId} skuId${skuId} 可下单); } return isAvailable; } async function monitorLoop(config, state) { while (!state.stop) { for (const item of config.goodsList) { const available await checkStock(item.goodsId, item.skuId, state.cookie); if (available) { // 加随机延时避免频繁请求触发风控同时防止多个商品同时命中导致并发下单 await sleep(1000 Math.random() * 3000); await orderService.process(item, state.cookie); } } // 每轮监控间隔 5~15 秒随机弱化请求频率特征 await sleep(5000 Math.random() * 10000); } }这里有个细节值得展开为什么到货之后还要 sleep 1~4 秒才下单因为真实用户从发现有货到点击加入购物车之间是有时间差的脚本如果 0 延迟立刻下单反而容易被识别。随机延时在这里不是为了慢而是为了让请求序列更接近自然人操作。另外一个细节是监控间隔的随机化——固定 5 秒的轮询看起来节奏感强但一旦平台察觉峰值 QPS很容易把你拉黑。3.3 下单模块加购、结算与订单提交下单模块是 jd-happy 全项目代码量最大的部分。它包括三个接口的串联分别是加入购物车、获取结算页信息、提交订单。这三个接口之间的依赖关系很严格任何一个环节拿不到预期响应都会中止流程。// lib/order.js async function submitOrder(item, cookie, addressId) { // 第一步加入购物车返回购物车内的商品 ID const cartResp await http.post(https://cart.example.com/add, { skuId: item.skuId, count: item.count }, { headers: { Cookie: cookie } }); if (!cartResp.data.success) throw new Error(加购失败); // 第二步访问结算页获取订单 token 和默认地址 const settleResp await http.get(https://trade.example.com/settle, { headers: { Cookie: cookie } }); const orderToken settleResp.data.token; // 第三步携带 token 提交订单 const orderResp await http.post(https://order.example.com/submit, { token: orderToken, addressId: addressId || settleResp.data.defaultAddressId, paymentType: 4 // 4 通常代表在线支付 }, { headers: { Cookie: cookie } }); return orderResp.data.orderId; }这段代码的逻辑是逐层校验的。第一步加购失败时说明商品状态可能已被其他用户抢占直接终止流程比继续往下走更高效第二步获取结算页信息时订单 token 是关键通行证这个字段由服务端动态生成无法提前伪造第三步提交时才真正创建订单。整个流程里最脆弱的环节是第二步——如果结算页响应里没有 token后面所有操作都无从谈起所以实际项目里这里通常还要加一层 token 存在性校验。4. 环境搭建与运行步骤从克隆到跑起来还能改配置4.1 本地运行环境要求与依赖安装这个项目跑起来的门槛其实很低。Node.js 环境 10.0 以上即可npm 安装依赖。进入项目目录后执行npm install依赖安装完成后核心要做的配置有两处。第一处是 config/index.js 里的商品信息包括 goodsId(商品页地址中的数字 ID)、skuId(具体规格的 ID)、购买数量第二处是 config/goods.json里面可以配置多个商品组成监控队列脚本会逐个轮询。# 修改配置后启动 node app.js启动后终端会显示一个二维码用手机 App 扫码确认登录登录成功后脚本就自动进入监控状态。所有日志输出到 logs/ 目录下按天分别记录方便第二天排查昨晚的监控结果。4.2 配置项逐项说明商品列表、请求间隔、购买数量打开 config/index.js你会看到一批可以直接改的参数这里把关键几项列出来配置项类型含义建议值goodsListArray待监控商品列表每个元素含 goodsId/skuId/count不宜超过 5 个intervalNumber两轮监控之间的基础间隔(毫秒)5000~15000retryTimesNumber单个接口请求失败时的最大重试次数3userAgentString请求时携带的 UA影响风控识别建议用真实浏览器 UAsaveCookieBoolean是否把登录 cookie 持久化到本地true修改间隔参数时有个实战经验如果目标商品热度很高比如限量球鞋、百亿补贴爆品间隔建议取 8000~15000 毫秒频率太高会被风控限制登录如果是冷门商品5000 毫秒轮询问题不大。另外购买数量不建议超过默认值部分平台会校验单账号单次限购。4.3 首次运行完成后如何看日志判断是否正常第一次跑完最需要关注的是 logs/ 下当天生成的日志文件。正常情况下应该看到类似这样的记录[2025-01-15 21:03:22] [INFO] 登录成功cookie有效期24小时 [2025-01-15 21:03:23] [INFO] 开始监控 goodsId1000123 skuId888888 [2025-01-15 21:03:28] [INFO] 当前状态无货继续监控 [2025-01-15 21:03:38] [WARN] 接口响应超时第 1 次重试如果出现反复重试或登录失效的提示先别急着改代码大概率是 cookie 过期或者请求频率太高。这种情况的处理方法在下面避坑章节详细讲。5. 常见问题与避坑记录登录失效、风控拦截、下单失败的现场还原5.1 现象登录成功后运行几分钟就显示登录失效重跑又要重新扫码原因这台机器或 IP 此前被平台风控标记过登录态下发后又被后台吊销。这种问题最容易出现在频繁切换网络或共享 IP 的情况下。解决不要反复重新扫码先检查是不是 cookie 持久化逻辑出了问题——确认 saveCookie 配置项为 true并且登录成功后本地 cokiie 文件正常写入。如果文件没生成多半是目录权限问题。如果文件正常生成但仍然失效换个网络环境(比如从 WiFi 切到手机热点)再试一次。5.2 现象监控模块一直输出无货但手动打开商品页明明显示有货原因平台对商品详情页和接口返回的数据做了差异化处理——页面端看到的是经过 CDN 缓存的有货而接口侧触发接口可能被限流导致长期拿到缓存数据。另一个常见原因是 skuId 填错不同颜色/规格对应不同 skuId填错时接口会返回该规格的独立库存状态。解决先抓包确认你访问的接口地址与商品页实际请求一致再核对 skuId 是否完全匹配。实在排查不出来把监控间隔调大一点再跑几轮有时平台的库存缓存更新周期是几分钟。5.3 现象下单流程跑到提交订单时报错 token 无效或不合法原因从结算页到提交订单之间token 有较短的有效期通常只有几分钟。脚本如果前面加了购后延时长或者结算页请求时网络慢token 就过期了。解决把加购、结算、提交这三步的串行逻辑改造成带超时重试的串联流程——结算页响应回来后token 存内存变量而不是重新读取提交失败时回到第二步重新获取 token最多重试 2 次。超过 2 次直接放弃本轮等下一轮轮询命中。5.4 现象本地开发环境一切正常部署到云服务器上却频繁被拦截原因云服务器 IP 通常是机房段电商平台对这类 IP 的信任度远低于家庭宽带更容易触发风控。解决尽量避免用数据中心 IP 运行如果必须用想办法降低请求频率并且把 User-Agent 换成真实浏览器版本不要用默认的 node-axios UA。日志里如果出现滑块验证或操作频繁字样的响应说明已经被风控盯上了这时最好的做法是暂停脚本冷却 30 分钟以上再跑。6. 二次开发与改造思路把监控京东扩展成通用到货提醒服务6.1 从单机脚本改成多商品监控队列这个项目目前的配置是基于本地 JSON 文件的改造方向之一是接入 Redis 做队列管理和状态存储。把商品列表从 config/goods.json 迁移到 Redis List 或 Set 中用另一个管理脚本往里 push 商品监控循环从队列中 pop 并执行这样就能实现运行时不改代码、动态增删监控商品。// 从 Redis 读取待监控商品替代静态 config 读取 const redis require(redis); const client redis.createClient(); async function getGoodsFromQueue() { // 每轮从队列中取 10 个商品pop 后重新放回队尾实现轮询 const goods await client.lrange(monitor:queue, 0, 9); return goods.map(item JSON.parse(item)); }这里我把商品队列设计为取出一批、处理完放回的模式好处是支持运行时动态调整坏处是队列会被无休止轮询。实际改造时我会在 Redis 里额外存一个状态字段标记商品是否被暂停监控暂停的商品直接从队列中滤除。6.2 把下单功能抽离成 Webhook 通知聚焦监控本身如果你并不需要自动下单只是想收到到货提醒最简单的改造是把 order.js 相关代码删掉在 checkStock 返回可用处加一个通知函数。推荐接入到 Server 酱、钉钉机器人或邮件通知。这一步无关乎平台限制很多监控场景下游并不希望你直接下单但及时通知能提高效率。实现思路如下// 改造 checkStock命中到货时发送通知而非下单 const notifier require(./notifier); async function checkStockAndNotify(goodsId, skuId, cookie) { const available await checkStock(goodsId, skuId, cookie); if (available) { // 这里调用通知服务消息内容带上商品链接和管理后台链接 await notifier.send({ title: 到货提醒, content: 商品 ${goodsId} 的 SKU ${skuId} 可购买了点击 https://item.example.com/${goodsId}.html 查看 }); } }这个改造把原本监控 下单的强耦合拆成了监控 提醒的弱耦合风险也大幅下降。下单功能如果后续还需要可以单独用浏览器自动化工具去解决而不是继续在纯 HTTP 请求层面跟风控死磕。6.3 我的改造血泪经验这个项目的价值不在直接跑通下单而在于它的分层思路——请求层、登录层、业务层互相隔离改造起来非常顺手。我第二次拿它改造时因为图快直接把监控逻辑塞进下单模块里结果前端频繁超时、后端接口时不时报错排查半天才发现是我把监控间隔参数覆盖了。从那以后我每次改造都会先看清楚 config 和 state 的分界线再动手改代码。希望帮到你。本文还有配套的精品资源点击获取