Maison  >  Article  >  développement back-end  >  Comment utiliser Python pour écrire un script de commande JD automatique ?

Comment utiliser Python pour écrire un script de commande JD automatique ?

WBOY
WBOYavant
2023-05-06 19:52:174312parcourir

1 Contexte du problème

Après d'innombrables achats échoués, j'ai découvert que les commerçants libéraient une petite quantité de stock de temps en temps, et j'ai estimé visuellement le nombre d'unités qu'il y aurait à chaque fois. Si nous écrivons un script pour surveiller l'inventaire des produits 24 heures sur 24 et que, une fois la source des marchandises interrogée, nous essaierons de passer des commandes automatiquement, ce qui peut augmenter considérablement nos chances de succès.

2 Idées de design

L'achat précipité de marchandises par JD est principalement divisé en deux types :

Achat sur rendez-vous : ouvert à l'achat sur place, ce qui est le même que le processus de commande de produits de vente flash ordinaires : interface d'achat séparée ; et le processus de commande.

Bien sûr, cette période est destinée aux achats urgents de réservation ou aux commandes en rupture de stock, c'est-à-dire que le processus de commande global est le même que lors de l'achat de produits ordinaires :

Connectez-vous à votre compte→ panier→ Sélectionnez le produit d'achat urgent→ Cliquez pour commander → Cliquez sur Soumettre la commande→ <code>登录账号 &rarr; 进入购物车 &rarr; 选择抢购商品 &rarr; 点击去结算 &rarr; 点击提交订单 &rarr; 选择付款方式并付款

3 具体实现

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

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

3.1 账号登录

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

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

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

获取登录二维码
def getQRcode(self):
    url = &#39;https://qr.m.jd.com/show&#39;
    payload = {
        &#39;appid&#39;: 133,
        &#39;size&#39;: 147,
        &#39;t&#39;: str(int(time.time() * 1000)),
    }
    headers = {
        &#39;User-Agent&#39;: self.userAgent,
        &#39;Referer&#39;: &#39;https://passport.jd.com/new/login.aspx&#39;,
    }
    resp = self.sess.get(url=url, headers=headers, params=payload)

    if not self.respStatus(resp):
        return None

    return resp.content
获取Ticket
def getQRcodeTicket(self):
    url = &#39;https://qr.m.jd.com/check&#39;
    payload = {
        &#39;appid&#39;: &#39;133&#39;,
        &#39;callback&#39;: &#39;jQuery{}&#39;.format(random.randint(1000000, 9999999)),
        &#39;token&#39;: self.sess.cookies.get(&#39;wlfstk_smdl&#39;),
        &#39;_&#39;: str(int(time.time() * 1000)),
    }
    headers = {
        &#39;User-Agent&#39;: self.userAgent,
        &#39;Referer&#39;: &#39;https://passport.jd.com/new/login.aspx&#39;,
    }
    resp = self.sess.get(url=url, headers=headers, params=payload)

    if not self.respStatus(resp):
        return False

    respJson = self.parseJson(resp.text)
    if respJson[&#39;code&#39;] != 200:
        return None
    else:
        return respJson[&#39;ticket&#39;]
验证 Ticket
def getQRcodeTicket(self):
    url = &#39;https://qr.m.jd.com/check&#39;
    payload = {
        &#39;appid&#39;: &#39;133&#39;,
        &#39;callback&#39;: &#39;jQuery{}&#39;.format(random.randint(1000000, 9999999)),
        &#39;token&#39;: self.sess.cookies.get(&#39;wlfstk_smdl&#39;),
        &#39;_&#39;: str(int(time.time() * 1000)),
    }
    headers = {
        &#39;User-Agent&#39;: self.userAgent,
        &#39;Referer&#39;: &#39;https://passport.jd.com/new/login.aspx&#39;,
    }
    resp = self.sess.get(url=url, headers=headers, params=payload)

    if not self.respStatus(resp):
        return False

    respJson = self.parseJson(resp.text)
    if respJson[&#39;code&#39;] != 200:
        return None
    else:
        return respJson[&#39;ticket&#39;]

此时验证 Ticket 有效后使用 pickle

3 Implémentation spécifique

Étant donné que l'auteur ne dispose pas de client capable de capturer des paquets, j'ai décidé d'utiliser l'interface WEB JD.com pour implémenter notre programme de script.

Ainsi, après avoir analysé le processus de commande sur la page Web de JD.com, nous avons divisé notre programme de script en quatre modules :
Module de connexion au compte, Module de surveillance des stocks
,
Module de gestion du panier, Module de gestion des commandes.

3.1 Connexion au compte

En raison des restrictions du code de vérification lors de l'utilisation des mots de passe du compte, scannez le code QR pour vous connecter afin de le contourner.
Les étudiants qui ne sont pas familiers ou intéressés par la connexion par code QR peuvent consulter le précédent article de blog de Zhou Zhou sur le principe et la mise en œuvre de la connexion par scan QR code.
Cette fois, il me suffit d'effectuer une analyse de capture de paquets sur la page de connexion JD et de trouver plusieurs interfaces utiles :
Obtenir le code QR de connexion
def getItemDetail(self, skuId):
    url = &#39;https://item.jd.com/{}.html&#39;.format(skuId)
    page = requests.get(url=url, headers=self.headers)

    html = etree.HTML(page.text)
    vender = html.xpath(
        &#39;//div[@class="follow J-follow-shop"]/@data-vid&#39;)[0]
    cat = html.xpath(&#39;//a[@clstag="shangpin|keycount|product|mbNav-3"]/@href&#39;)[
        0].replace(&#39;//list.jd.com/list.html?cat=&#39;, &#39;&#39;)

    if not vender or not cat:
        raise Exception(&#39;获取商品信息失败,请检查SKU是否正确&#39;)

    detail = dict(catId=cat, venderId=vender)
    return detail
Obtenir un ticket

def getItemStock(self, skuId, num, areaId):

    item = self.itemDetails.get(skuId)

    if not item:
        return False

    url = &#39;https://c0.3.cn/stock&#39;
    payload = {
        &#39;skuId&#39;: skuId,
        &#39;buyNum&#39;: num,
        &#39;area&#39;: areaId,
        &#39;ch&#39;: 1,
        &#39;_&#39;: str(int(time.time() * 1000)),
        &#39;callback&#39;: &#39;jQuery{}&#39;.format(random.randint(1000000, 9999999)),
        # get error stock state without this param
        &#39;extraParam&#39;: &#39;{"originid":"1"}&#39;,
        # get 403 Forbidden without this param (obtained from the detail page)
        &#39;cat&#39;: item.get(&#39;catId&#39;),
        # return seller information with this param (can&#39;t be ignored)
        &#39;venderId&#39;: item.get(&#39;venderId&#39;)
    }
    headers = {
        &#39;User-Agent&#39;: self.userAgent,
        &#39;Referer&#39;: &#39;https://item.jd.com/{}.html&#39;.format(skuId),
    }

    respText = &#39;&#39;
    try:
        respText = requests.get(
            url=url, params=payload, headers=headers, timeout=self.timeout).text
        respJson = self.parseJson(respText)
        stockInfo = respJson.get(&#39;stock&#39;)
        skuState = stockInfo.get(&#39;skuState&#39;)  # 商品是否上架
        # 商品库存状态:33 -- 现货  0,34 -- 无货  36 -- 采购中  40 -- 可配货
        stockState = stockInfo.get(&#39;StockState&#39;)
        return skuState == 1 and stockState in (33, 40)

Verify Ticket

def uncheckCartAll(self):
    """ 取消所有选中商品
    return 购物车信息
    """
    url = &#39;https://api.m.jd.com/api&#39;

    headers = {
        &#39;User-Agent&#39;: self.userAgent,
        &#39;Content-Type&#39;: &#39;application/x-www-form-urlencoded&#39;,
        &#39;origin&#39;: &#39;https://cart.jd.com&#39;,
        &#39;referer&#39;: &#39;https://cart.jd.com&#39;
    }

    data = {
        &#39;functionId&#39;: &#39;pcCart_jc_cartUnCheckAll&#39;,
        &#39;appid&#39;: &#39;JDC_mall_cart&#39;,
        &#39;body&#39;: &#39;{"serInfo":{"area":"","user-key":""}}&#39;,
        &#39;loginType&#39;: 3
    }

    resp = self.sess.post(url=url, headers=headers, data=data)

    # return self.respStatus(resp) and resp.json()[&#39;success&#39;]
    return resp

Verify Ticket à ce moment Après validation, utilisez la bibliothèque pickle pour enregistrer localement les cookies dans la session du programme pour une prochaine utilisation.
3.2 Surveillance des stocks
La surveillance des stocks est relativement simple, analysez la page de détails du produit, obtenez l'ID du magasin et les attributs de classification du produit :
Obtenez des informations sur les détails du produit
🎜
def addCartSku(self, skuId, skuNum):
    """ 加入购入车
    skuId 商品sku
    skuNum 购买数量
    retrun 是否成功
    """
    url = &#39;https://api.m.jd.com/api&#39;
    headers = {
        &#39;User-Agent&#39;: self.userAgent,
        &#39;Content-Type&#39;: &#39;application/x-www-form-urlencoded&#39;,
        &#39;origin&#39;: &#39;https://cart.jd.com&#39;,
        &#39;referer&#39;: &#39;https://cart.jd.com&#39;
    }
    data = {
        &#39;functionId&#39;: &#39;pcCart_jc_cartAdd&#39;,
        &#39;appid&#39;: &#39;JDC_mall_cart&#39;,
        &#39;body&#39;: &#39;{\"operations\":[{\"carttype\":1,\"TheSkus\":[{\"Id\":\"&#39; + skuId + &#39;\",\"num\":&#39; + str(skuNum) + &#39;}]}]}&#39;,
        &#39;loginType&#39;: 3
    }
    resp = self.sess.post(url=url, headers=headers, data=data)
    return self.respStatus(resp) and resp.json()[&#39;success&#39;]
🎜🎜Interrogez l'inventaire🎜🎜
def changeCartSkuCount(self, skuId, skuUid, skuNum, areaId):
    """ 修改购物车商品数量
    skuId 商品sku
    skuUid 商品用户关系
    skuNum 购买数量
    retrun 是否成功
    """
    url = &#39;https://api.m.jd.com/api&#39;
    headers = {
        &#39;User-Agent&#39;: self.userAgent,
        &#39;Content-Type&#39;: &#39;application/x-www-form-urlencoded&#39;,
        &#39;origin&#39;: &#39;https://cart.jd.com&#39;,
        &#39;referer&#39;: &#39;https://cart.jd.com&#39;
    }
    body = &#39;{\"operations\":[{\"TheSkus\":[{\"Id\":\"&#39;+skuId+&#39;\",\"num\":&#39;+str(
        skuNum)+&#39;,\"skuUuid\":\"&#39;+skuUid+&#39;\",\"useUuid\":false}]}],\"serInfo\":{\"area\":\"&#39;+areaId+&#39;\"}}&#39;
    data = {
        &#39;functionId&#39;: &#39;pcCart_jc_changeSkuNum&#39;,
        &#39;appid&#39;: &#39;JDC_mall_cart&#39;,
        &#39;body&#39;: body,
        &#39;loginType&#39;: 3
    }
    resp = self.sess.post(url=url, headers=headers, data=data)
    return self.respStatus(resp) and resp.json()[&#39;success&#39;]
🎜3.3 Fonctionnement du panier 🎜🎜Aucun Nous ne pouvons pas ajouter d'articles en stock au panier via l'opération de page. Nous pouvons utiliser d'autres articles en stock pour essayer ici. Nous vérifions principalement l'interface d'ajout, de suppression, de modification et de vérification du panier : 🎜🎜. 🎜Annuler tous les articles sélectionnés🎜🎜
def getCheckoutPage(self):
    """获取订单结算页面信息
    :return: 结算信息 dict
    """
    url = &#39;http://trade.jd.com/shopping/order/getOrderInfo.action&#39;
    # url = &#39;https://cart.jd.com/gotoOrder.action&#39;
    payload = {
        &#39;rid&#39;: str(int(time.time() * 1000)),
    }
    headers = {
        &#39;User-Agent&#39;: self.userAgent,
        &#39;Referer&#39;: &#39;https://cart.jd.com/cart&#39;,
    }
🎜🎜Ajouter à l'achat Entrez dans le panier🎜🎜
def submitOrder(self):
    """提交订单
    :return: True/False 订单提交结果
    """
    url = &#39;https://trade.jd.com/shopping/order/submitOrder.action&#39;
    # js function of submit order is included in https://trade.jd.com/shopping/misc/js/order.js?r=2018070403091

    data = {
        &#39;overseaPurchaseCookies&#39;: &#39;&#39;,
        &#39;vendorRemarks&#39;: &#39;[]&#39;,
        &#39;submitOrderParam.sopNotPutInvoice&#39;: &#39;false&#39;,
        &#39;submitOrderParam.trackID&#39;: &#39;TestTrackId&#39;,
        &#39;submitOrderParam.ignorePriceChange&#39;: &#39;0&#39;,
        &#39;submitOrderParam.btSupport&#39;: &#39;0&#39;,
        &#39;riskControl&#39;: self.risk_control,
        &#39;submitOrderParam.isBestCoupon&#39;: 1,
        &#39;submitOrderParam.jxj&#39;: 1,
        &#39;submitOrderParam.trackId&#39;: self.track_id,
        &#39;submitOrderParam.eid&#39;: self.eid,
        &#39;submitOrderParam.fp&#39;: self.fp,
        &#39;submitOrderParam.needCheck&#39;: 1,
    }
🎜🎜Modifier le nombre d'articles dans le panier🎜🎜rrreee🎜Ce qui précède est l'interface minimale que nous devons utiliser pour un achat. pour ne pas détruire les données existantes dans le panier du compte, suivez les étapes suivantes pour préparer le panier : 🎜🎜Annuler Tout vérifier (retour aux informations du panier) ; modifier la quantité du produit s'il est déjà dans le panier ; ajoutez-le au panier s'il n'y est pas. 3.4 Opérations de commande🎜🎜Après avoir préparé le panier (sélectionner les articles à acheter et ajuster la quantité achetée), nous pouvons procéder aux prochaines opérations liées à la commande : 🎜🎜🎜Obtenir le formulaire de règlement🎜🎜rrreee🎜🎜Soumettre la commande 🎜🎜rrreee

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer