文章目录

  • 1 问题背景
  • 2 设计思路
  • 3 具体实现
    • 3.1 账号登录
      • 获取登录二维码
      • 获取Ticket
      • 验证 Ticket
    • 3.2 库存监听
      • 获取商品详情信息
      • 查询库存
    • 3.3 购物车操作
      • 取消所有选中商品
      • 加入购入车
      • 修改购物车商品数量
    • 3.4 订单操作
      • 获取结算单
      • 提交订单
    • 3.5 其他模块
      • 3.5.1 微信通知
      • 3.5.2 日志模块
      • 3.5.3 打包成APP模块
      • 3.5.4 参数模块
  • 4 参考文献

1 问题背景

编写脚本监听商品库存,一旦查询到货源便开始尝试自动下单。

2 设计思路

京东对于商品的抢购主要分为两种:

  • 预约抢购:到点开放购买,和普通商品下单流程一致;
  • 秒杀商品:单独的抢购接口和下单流程。

当然本次针对的预约抢购类或无货订购类,即整体下单流程和购买普通商品时一样:

登录账号 → 进入购物车 → 选择抢购商品 → 点击去结算 → 点击提交订单 → 选择付款方式并付款

3 具体实现

采用京东 WEB 端接口实现我们的脚本程序。

于是经过对京东网页下单流程的分析,将我们的脚本程序分为四个模块:

  • 账号登录模块
  • 库存监听模块
  • 购物车管理模块
  • 订单管理模块

3.1 账号登录

由于使用账号密码时有验证码限制,此处采用扫码登录方式绕过。

本次主要针对京东登录页进行抓包分析,找到几个有用接口.

获取登录二维码
def getQRcode(self):url = 'https://qr.m.jd.com/show'payload = {'appid': 133,'size': 147,'t': str(int(time.time() * 1000)),}headers = {'User-Agent': self.userAgent,'Referer': 'https://passport.jd.com/new/login.aspx',}resp = self.sess.get(url=url, headers=headers, params=payload)if not self.respStatus(resp):return Nonereturn resp.content
获取Ticket
def getQRcodeTicket(self):url = 'https://qr.m.jd.com/check'payload = {'appid': '133','callback': 'jQuery{}'.format(random.randint(1000000, 9999999)),'token': self.sess.cookies.get('wlfstk_smdl'),'_': str(int(time.time() * 1000)),}headers = {'User-Agent': self.userAgent,'Referer': 'https://passport.jd.com/new/login.aspx',}resp = self.sess.get(url=url, headers=headers, params=payload)if not self.respStatus(resp):return FalserespJson = self.parseJson(resp.text)if respJson['code'] != 200:return Noneelse:return respJson['ticket']
验证 Ticket
def validateQRcodeTicket(self, ticket):url = 'https://passport.jd.com/uc/qrCodeTicketValidation'headers = {'User-Agent': self.userAgent,'Referer': 'https://passport.jd.com/uc/login?ltype=logout',}resp = self.sess.get(url=url, headers=headers, params={'t': ticket})if not self.respStatus(resp):return FalserespJson = json.loads(resp.text)if respJson['returnCode'] == 0:return Trueelse:return False

此时验证 Ticket 有效后使用 pickle 库将程序会话中的 cookie 保存到本地以便下次使用。

3.2 库存监听

库存监听较为简单,分析商品详情页,获取店铺ID以及商品分类属性

获取商品详情信息
def getItemDetail(self, skuId):url = 'https://item.jd.com/{}.html'.format(skuId)page = requests.get(url=url, headers=self.headers)html = etree.HTML(page.text)vender = html.xpath('//div[@class="follow J-follow-shop"]/@data-vid')[0]cat = html.xpath('//a[@clstag="shangpin|keycount|product|mbNav-3"]/@href')[0].replace('//list.jd.com/list.html?cat=', '')if not vender or not cat:raise Exception('获取商品信息失败,请检查SKU是否正确')detail = dict(catId=cat, venderId=vender)return detail
查询库存
def getItemStock(self, skuId, num, areaId):item = self.itemDetails.get(skuId)if not item:return Falseurl = 'https://c0.3.cn/stock'payload = {'skuId': skuId,'buyNum': num,'area': areaId,'ch': 1,'_': str(int(time.time() * 1000)),'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)),# get error stock state without this param'extraParam': '{"originid":"1"}',# get 403 Forbidden without this param (obtained from the detail page)'cat': item.get('catId'),# return seller information with this param (can't be ignored)'venderId': item.get('venderId')}headers = {'User-Agent': self.userAgent,'Referer': 'https://item.jd.com/{}.html'.format(skuId),}respText = ''try:respText = requests.get(url=url, params=payload, headers=headers, timeout=self.timeout).textrespJson = self.parseJson(respText)stockInfo = respJson.get('stock')skuState = stockInfo.get('skuState')  # 商品是否上架# 商品库存状态:33 -- 现货  0,34 -- 无货  36 -- 采购中  40 -- 可配货stockState = stockInfo.get('StockState')return skuState == 1 and stockState in (33, 40)

3.3 购物车操作

无货商品加入到购物车我们是无法通过页面操作的,我们这边可以使用其他有货商品进行尝试,主要查看购物车的增删改查接口:

取消所有选中商品
def uncheckCartAll(self):""" 取消所有选中商品return 购物车信息"""url = 'https://api.m.jd.com/api'headers = {'User-Agent': self.userAgent,'Content-Type': 'application/x-www-form-urlencoded','origin': 'https://cart.jd.com','referer': 'https://cart.jd.com'}data = {'functionId': 'pcCart_jc_cartUnCheckAll','appid': 'JDC_mall_cart','body': '{"serInfo":{"area":"","user-key":""}}','loginType': 3}resp = self.sess.post(url=url, headers=headers, data=data)# return self.respStatus(resp) and resp.json()['success']return resp
加入购入车
def addCartSku(self, skuId, skuNum):""" 加入购入车skuId 商品skuskuNum 购买数量retrun 是否成功"""url = 'https://api.m.jd.com/api'headers = {'User-Agent': self.userAgent,'Content-Type': 'application/x-www-form-urlencoded','origin': 'https://cart.jd.com','referer': 'https://cart.jd.com'}data = {'functionId': 'pcCart_jc_cartAdd','appid': 'JDC_mall_cart','body': '{\"operations\":[{\"carttype\":1,\"TheSkus\":[{\"Id\":\"' + skuId + '\",\"num\":' + str(skuNum) + '}]}]}','loginType': 3}resp = self.sess.post(url=url, headers=headers, data=data)return self.respStatus(resp) and resp.json()['success']
修改购物车商品数量
def changeCartSkuCount(self, skuId, skuUid, skuNum, areaId):""" 修改购物车商品数量skuId 商品skuskuUid 商品用户关系skuNum 购买数量retrun 是否成功"""url = 'https://api.m.jd.com/api'headers = {'User-Agent': self.userAgent,'Content-Type': 'application/x-www-form-urlencoded','origin': 'https://cart.jd.com','referer': 'https://cart.jd.com'}body = '{\"operations\":[{\"TheSkus\":[{\"Id\":\"'+skuId+'\",\"num\":'+str(skuNum)+',\"skuUuid\":\"'+skuUid+'\",\"useUuid\":false}]}],\"serInfo\":{\"area\":\"'+areaId+'\"}}'data = {'functionId': 'pcCart_jc_changeSkuNum','appid': 'JDC_mall_cart','body': body,'loginType': 3}resp = self.sess.post(url=url, headers=headers, data=data)return self.respStatus(resp) and resp.json()['success']

以上是我们一次购买需要用到的最少接口,为了不破坏账户购物车中已有数据,采用以下步骤准备好购物车:

  • 取消全部勾选(返回购物车信息);
  • 已在购物车则修改商品数量;
  • 不在购物车则加入购物车。

3.4 订单操作

当我们准备好购物车之后(选中购买商品以及调整购买数量),就可以进行下一步订单相关操作:

获取结算单
def getCheckoutPage(self):"""获取订单结算页面信息:return: 结算信息 dict"""url = 'http://trade.jd.com/shopping/order/getOrderInfo.action'# url = 'https://cart.jd.com/gotoOrder.action'payload = {'rid': str(int(time.time() * 1000)),}headers = {'User-Agent': self.userAgent,'Referer': 'https://cart.jd.com/cart',}
提交订单
def submitOrder(self):"""提交订单:return: True/False 订单提交结果"""url = 'https://trade.jd.com/shopping/order/submitOrder.action'# js function of submit order is included in https://trade.jd.com/shopping/misc/js/order.js?r=2018070403091data = {'overseaPurchaseCookies': '','vendorRemarks': '[]','submitOrderParam.sopNotPutInvoice': 'false','submitOrderParam.trackID': 'TestTrackId','submitOrderParam.ignorePriceChange': '0','submitOrderParam.btSupport': '0','riskControl': self.risk_control,'submitOrderParam.isBestCoupon': 1,'submitOrderParam.jxj': 1,'submitOrderParam.trackId': self.track_id,'submitOrderParam.eid': self.eid,'submitOrderParam.fp': self.fp,'submitOrderParam.needCheck': 1,}

3.5 其他模块

3.5.1 微信通知

抢购成功增加微信通知

def send_wechat(message, desp, sckey):"""server酱微信通知:param message::param desp: 发送内容:param sckey: 密钥:return:"""if not message.strip():logger.error('Text of message is empty!')returnnow_time = str(datetime.datetime.now())desp = '[{0}]'.format(now_time) if not desp else '{0} [{1}]'.format(desp, now_time)try:resp = requests.get('https://sc.ftqq.com/{}.send?text={}&desp={}'.format(sckey, message, desp))resp_json = json.loads(resp.text)if resp_json['data']['errno'] == 0:logger.info('Message sent successfully [text: %s, desp: %s]', message, desp)else:logger.error('Fail to send message, reason: %s', resp.text)except requests.exceptions.RequestException as req_error:logger.error('Request error: %s', req_error)except Exception as e:logger.error('Fail to send message [text: %s, desp: %s]: %s', message, desp, e)
3.5.2 日志模块
#!/usr/bin/env python
# -*- encoding=utf8 -*-
import logging
import logging.handlers
import os
from time import strftimeLOG_FILENAME = strftime("logs\jd-buyer_%Y_%m_%d_%H.log")
logger = logging.getLogger()def set_logger():path = os.path.dirname(os.getcwd()+ '\\logs\\') # 判断日志目录if not os.path.exists(path):os.makedirs(path)logger.setLevel(logging.INFO)formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')console_handler = logging.StreamHandler()console_handler.setFormatter(formatter)logger.addHandler(console_handler)file_handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=10485760, backupCount=5, encoding="utf-8")file_handler.setFormatter(formatter)logger.addHandler(file_handler)
set_logger()
3.5.3 打包成APP模块

可以参考pyinstaller或者auto-py-to-exe用法

3.5.4 参数模块
{"skuId": "10060659038775","areaId": "1_2901_4135","count": 9,"stockInterval": 9,"submitInterval": 0,"buyTime": "","password": ""
}
[messenger]
# 使用了Server酱的推送服务
# 如果想开启下单成功后消息推送,则将 enable 设置为 true,默认为 false 不开启推送
# 开启消息推送必须填入 sckey,如何获取请参考 http://sc.ftqq.com/3.version。感谢Server酱~
enable = true
sckey = ""

4 参考文献

  • https://blog.csdn.net/adminpd/article/details/127188898

【Python】京东抢购脚本相关推荐

  1. python京东抢购脚本_五个月抢京东抢茅台心得

    5个月抢到4瓶茅台分享一点心得. 目前网上抢茅台靠谱且门槛低的有三个,首先是天猫,无门槛,实名认证,余额够就行.然后苏宁易购,一三五非会员也能抢购.其它时间要开会员才行.最后京东,必须plus会员.其 ...

  2. 双十一就要到啦,教你用Python制作抢购脚本,秒杀一切商品

    想要学习Python.关注小编,私信[学习资料],即可免费领取一整套系统的板Python学习教程! 今天简单介绍一下python怎么实现京东抢购脚本. 严格意义上来说,这个都不算是抢购. 因为根本就没 ...

  3. [转载] Python京东抢购

    参考链接: 从Python获取输入 Python京东抢购 分析其中提交信息接口的参数,可以成功抢购商品,并且可以提交订单....2018年7月17日 提交信息的获取 直接提交信息对post提交分析其中 ...

  4. Python京东抢购

    Python京东抢购 分析其中提交信息接口的参数,可以成功抢购商品,并且可以提交订单....2018年7月17日 提交信息的获取 直接提交信息对post提交分析其中的参数. 经过分析参数大多数在:ht ...

  5. 【Python爬虫脚本】拒绝黄牛从我做起!Python自动抢购脚本,准点原价秒杀飞天茅台!!!

    前言 点击下方视频即可查看详细教程 [Python爬虫脚本]拒绝黄牛从我做起!Python自动抢购脚本,准点原价秒杀飞天茅台!!! 源码领取 上述这份完整版的源码课件已经上传CSDN官方,朋友们如果需 ...

  6. python 梦幻西游脚本_python京东抢购脚本打包版下载-京东抢购茅台Python打包版1.0.0简易版下载_骑士下载...

    京东抢购茅台Python打包版是一款超级好用的脚本打包版,因为有很多的用户都有这个抢购茅台的需求,但是自己不会Python,看不懂代码,这里小编就给大家带来最新的打包版,让玩家可以轻松简单的抢到茅台. ...

  7. python京东抢购 github_两天狂揽 4k+ Star,抢茅台 Python 脚本霸榜 GitHub

    原标题:两天狂揽 4k+ Star,抢茅台 Python 脚本霸榜 GitHub 声明:本文已获得 开源前哨 授权 某东抢茅台脚本在 GitHub 连续霸榜 30 日,前哨君在刷 GitHub 的时候 ...

  8. 使用Python茅台抢购脚本的使用说明

    京东抢购流程 本文我介绍两种不同的方法方法二适合小白学习 文章有点长请耐心看完 方法一 简单明了的流程图 首先来说说京东的吧. 画个简单的流程图: null和90008 在流程图中,充分展示了为什么有 ...

  9. 京东抢购脚本使用方法+代码更新(超详细)

    最近一段时间,很多人在京东抢购茅台的过程中,由于不会搭建脚本的环境,因此望而却步.因此,特地写了这篇文章,一步一步指导小白如何完成python环境的搭建. 注意,本文采用的是最基础的pyharm搭建, ...

  10. python京东抢购软件神器_用Python在京东抢购商品

    最近在网上看到一篇运用python从京东抢购商品的博客,激起了我的好奇心,python还真的啥都能做到吗????一起来跟小编尝试一下~ 首先肯定是安装python环境,大家可以从官网上下载最新版的py ...

最新文章

  1. LabVIEW机器视觉系统图像畸变、校准和矫正(基础篇—3)
  2. ASP.NET MVC WebAPI实现文件批量上传
  3. linux 查找目录或文件
  4. 第九节:JWT简介和以JS+WebApi为例基于JWT的安全校验
  5. SpringMVC文件上传(三)异常栈处理
  6. JS - 移动设备终端的touch事件
  7. 你知道地球上还剩多少石油吗?
  8. python对文件操作的统一步骤_基于Python实现对各种数据文件的操作
  9. “VMRC控制台的连接已断开…正在尝试重新连接”的解决方法
  10. IIR滤波器的FPGA实现
  11. Flir Blackfly S USB3 工业相机:白平衡设置方法
  12. 单片机简易数字钟c语言程序,用AT89C51/AT89S52单片机制作的简易数字钟
  13. phpstudy开机自启
  14. 工作十年的程序员,却拿着毕业三年的工资……
  15. Dubbo 第一章:详情版
  16. 用户登录 验证数据库
  17. 纯css实现文字跳动的动画效果
  18. 数据的存储,大端存储和小端小端存储
  19. Spring 源码分析(一) —— 迈向Spring之路
  20. vue 中报错 [Element Migrating][ElDialog][Attribute]: size is removed.

热门文章

  1. 基于SSM+SpringBoot的宠物医疗治疗管理系统
  2. AcWing 1143. 联络员
  3. OSChina 周六乱弹 —— 这是我的新秘书想看吧
  4. 国内外顶尖人工智能实验室
  5. 【颜纠日记】你还不会用百度搜索吗?搜索引擎关键词技巧宝典。
  6. 基于asp.net320班级同学录网站
  7. 基于JavaEE的同学录校友录管理系统_JSP网站设计_SqlServer数据库设计
  8. 如何计算机网络打印机驱动程序,电脑怎么安装打印机及打印机的驱动程序
  9. 安卓apk在CentOS上自动化编译打包发布
  10. [macos - git commit] wasm code commit Allocation failed - process out of memory