怎么用Python写一个京东自动下单抢购脚本

其他教程   发布日期:2023年10月25日   浏览次数:941

本文小编为大家详细介绍“怎么用Python写一个京东自动下单抢购脚本”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么用Python写一个京东自动下单抢购脚本”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

1 问题背景

经过无数次抢购失败后,发现商家会不定时的放出少量货源,目测每次会有几台。如果我们编写一个脚本程序24小时不间断监听商品库存,一旦查询到货源便开始尝试自动下单,这样就可以极大提高我们的成功概率。

2 设计思路

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

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

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

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

3 具体实现

由于笔者本人没有一个可以抓包的客户端,决定采用京东 WEB 端接口实现我们的脚本程序。

于是经过对京东网页下单流程的分析,将我们的脚本程序分为四个模块:账号登录模块库存监听模块购物车管理模块订单管理模块。

3.1 账号登录

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

如对扫码登录不熟悉或感兴趣的同学可以查看周周之前的博文 扫码登录原理和实现。

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

获取登录二维码

  1. def getQRcode(self):
  2. url = 'https://qr.m.jd.com/show'
  3. payload = {
  4. 'appid': 133,
  5. 'size': 147,
  6. 't': str(int(time.time() * 1000)),
  7. }
  8. headers = {
  9. 'User-Agent': self.userAgent,
  10. 'Referer': 'https://passport.jd.com/new/login.aspx',
  11. }
  12. resp = self.sess.get(url=url, headers=headers, params=payload)
  13. if not self.respStatus(resp):
  14. return None
  15. return resp.content

获取Ticket

  1. def getQRcodeTicket(self):
  2. url = 'https://qr.m.jd.com/check'
  3. payload = {
  4. 'appid': '133',
  5. 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)),
  6. 'token': self.sess.cookies.get('wlfstk_smdl'),
  7. '_': str(int(time.time() * 1000)),
  8. }
  9. headers = {
  10. 'User-Agent': self.userAgent,
  11. 'Referer': 'https://passport.jd.com/new/login.aspx',
  12. }
  13. resp = self.sess.get(url=url, headers=headers, params=payload)
  14. if not self.respStatus(resp):
  15. return False
  16. respJson = self.parseJson(resp.text)
  17. if respJson['code'] != 200:
  18. return None
  19. else:
  20. return respJson['ticket']

验证 Ticket

  1. def getQRcodeTicket(self):
  2. url = 'https://qr.m.jd.com/check'
  3. payload = {
  4. 'appid': '133',
  5. 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)),
  6. 'token': self.sess.cookies.get('wlfstk_smdl'),
  7. '_': str(int(time.time() * 1000)),
  8. }
  9. headers = {
  10. 'User-Agent': self.userAgent,
  11. 'Referer': 'https://passport.jd.com/new/login.aspx',
  12. }
  13. resp = self.sess.get(url=url, headers=headers, params=payload)
  14. if not self.respStatus(resp):
  15. return False
  16. respJson = self.parseJson(resp.text)
  17. if respJson['code'] != 200:
  18. return None
  19. else:
  20. return respJson['ticket']

此时验证 Ticket 有效后使用

  1. pickle
库将程序会话中的 cookie 保存到本地以便下次使用。

3.2 库存监听

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

获取商品详情信息

  1. def getItemDetail(self, skuId):
  2. url = 'https://item.jd.com/{}.html'.format(skuId)
  3. page = requests.get(url=url, headers=self.headers)
  4. html = etree.HTML(page.text)
  5. vender = html.xpath(
  6. '//div[@class="follow J-follow-shop"]/@data-vid')[0]
  7. cat = html.xpath('//a[@clstag="shangpin|keycount|product|mbNav-3"]/@href')[
  8. 0].replace('//list.jd.com/list.html?cat=', '')
  9. if not vender or not cat:
  10. raise Exception('获取商品信息失败,请检查SKU是否正确')
  11. detail = dict(catId=cat, venderId=vender)
  12. return detail

查询库存

  1. def getItemStock(self, skuId, num, areaId):
  2. item = self.itemDetails.get(skuId)
  3. if not item:
  4. return False
  5. url = 'https://c0.3.cn/stock'
  6. payload = {
  7. 'skuId': skuId,
  8. 'buyNum': num,
  9. 'area': areaId,
  10. 'ch': 1,
  11. '_': str(int(time.time() * 1000)),
  12. 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)),
  13. # get error stock state without this param
  14. 'extraParam': '{"originid":"1"}',
  15. # get 403 Forbidden without this param (obtained from the detail page)
  16. 'cat': item.get('catId'),
  17. # return seller information with this param (can't be ignored)
  18. 'venderId': item.get('venderId')
  19. }
  20. headers = {
  21. 'User-Agent': self.userAgent,
  22. 'Referer': 'https://item.jd.com/{}.html'.format(skuId),
  23. }
  24. respText = ''
  25. try:
  26. respText = requests.get(
  27. url=url, params=payload, headers=headers, timeout=self.timeout).text
  28. respJson = self.parseJson(respText)
  29. stockInfo = respJson.get('stock')
  30. skuState = stockInfo.get('skuState') # 商品是否上架
  31. # 商品库存状态:33 -- 现货 0,34 -- 无货 36 -- 采购中 40 -- 可配货
  32. stockState = stockInfo.get('StockState')
  33. return skuState == 1 and stockState in (33, 40)

3.3 购物车操作

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

取消所有选中商品

  1. def uncheckCartAll(self):
  2. """ 取消所有选中商品
  3. return 购物车信息
  4. """
  5. url = 'https://api.m.jd.com/api'
  6. headers = {
  7. 'User-Agent': self.userAgent,
  8. 'Content-Type': 'application/x-www-form-urlencoded',
  9. 'origin': 'https://cart.jd.com',
  10. 'referer': 'https://cart.jd.com'
  11. }
  12. data = {
  13. 'functionId': 'pcCart_jc_cartUnCheckAll',
  14. 'appid': 'JDC_mall_cart',
  15. 'body': '{"serInfo":{"area":"","user-key":""}}',
  16. 'loginType': 3
  17. }
  18. resp = self.sess.post(url=url, headers=headers, data=data)
  19. # return self.respStatus(resp) and resp.json()['success']
  20. return resp

加入购入车

  1. def addCartSku(self, skuId, skuNum):
  2. """ 加入购入车
  3. skuId 商品sku
  4. skuNum 购买数量
  5. retrun 是否成功
  6. """
  7. url = 'https://api.m.jd.com/api'
  8. headers = {
  9. 'User-Agent': self.userAgent,
  10. 'Content-Type': 'application/x-www-form-urlencoded',
  11. 'origin': 'https://cart.jd.com',
  12. 'referer': 'https://cart.jd.com'
  13. }
  14. data = {
  15. 'functionId': 'pcCart_jc_cartAdd',
  16. 'appid': 'JDC_mall_cart',
  17. 'body': '{"operations":[{"carttype":1,"TheSkus":[{"Id":"' + skuId + '","num":' + str(skuNum) + '}]}]}',
  18. 'loginType': 3
  19. }
  20. resp = self.sess.post(url=url, headers=headers, data=data)
  21. return self.respStatus(resp) and resp.json()['success']

修改购物车商品数量

  1. def changeCartSkuCount(self, skuId, skuUid, skuNum, areaId):
  2. """ 修改购物车商品数量
  3. skuId 商品sku
  4. skuUid 商品用户关系
  5. skuNum 购买数量
  6. retrun 是否成功
  7. """
  8. url = 'https://api.m.jd.com/api'
  9. headers = {
  10. 'User-Agent': self.userAgent,
  11. 'Content-Type': 'application/x-www-form-urlencoded',
  12. 'origin': 'https://cart.jd.com',
  13. 'referer': 'https://cart.jd.com'
  14. }
  15. body = '{"operations":[{"TheSkus":[{"Id":"'+skuId+'","num":'+str(
  16. skuNum)+',"skuUuid":"'+skuUid+'","useUuid":false}]}],"serInfo":{"area":"'+areaId+'"}}'
  17. data = {
  18. 'functionId': 'pcCart_jc_changeSkuNum',
  19. 'appid': 'JDC_mall_cart',
  20. 'body': body,
  21. 'loginType': 3
  22. }
  23. resp = self.sess.post(url=url, headers=headers, data=data)
  24. return self.respStatus(resp) and resp.json()['success']

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

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

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

获取结算单

  1. def getCheckoutPage(self):
  2. """获取订单结算页面信息
  3. :return: 结算信息 dict
  4. """
  5. url = 'http://trade.jd.com/shopping/order/getOrderInfo.action'
  6. # url = 'https://cart.jd.com/gotoOrder.action'
  7. payload = {
  8. 'rid': str(int(time.time() * 1000)),
  9. }
  10. headers = {
  11. 'User-Agent': self.userAgent,
  12. 'Referer': 'https://cart.jd.com/cart',
  13. }

提交订单

  1. def submitOrder(self):
  2. """提交订单
  3. :return: True/False 订单提交结果
  4. """
  5. url = 'https://trade.jd.com/shopping/order/submitOrder.action'
  6. # js function of submit order is included in https://trade.jd.com/shopping/misc/js/order.js?r=2018070403091
  7. data = {
  8. 'overseaPurchaseCookies': '',
  9. 'vendorRemarks': '[]',
  10. 'submitOrderParam.sopNotPutInvoice': 'false',
  11. 'submitOrderParam.trackID': 'TestTrackId',
  12. 'submitOrderParam.ignorePriceChange': '0',
  13. 'submitOrderParam.btSupport': '0',
  14. 'riskControl': self.risk_control,
  15. 'submitOrderParam.isBestCoupon': 1,
  16. 'submitOrderParam.jxj': 1,
  17. 'submitOrderParam.trackId': self.track_id,
  18. 'submitOrderParam.eid': self.eid,
  19. 'submitOrderParam.fp': self.fp,
  20. 'submitOrderParam.needCheck': 1,
  21. }

以上就是怎么用Python写一个京东自动下单抢购脚本的详细内容,更多关于怎么用Python写一个京东自动下单抢购脚本的资料请关注九品源码其它相关文章!