Heim >Backend-Entwicklung >Python-Tutorial >Wie schreibe ich mit Python ein automatisches JD-Bestellskript?

Wie schreibe ich mit Python ein automatisches JD-Bestellskript?

WBOY
WBOYnach vorne
2023-05-06 19:52:174515Durchsuche

1 Hintergrund des Problems

Nach zahlreichen fehlgeschlagenen Käufen stellte ich fest, dass Händler von Zeit zu Zeit eine kleine Menge an Vorräten freigaben, und ich konnte visuell abschätzen, wie viele Einheiten jeweils vorhanden sein würden. Wenn wir ein Skript schreiben, um den Produktbestand 24 Stunden am Tag zu überwachen, und sobald die Warenquelle abgefragt wird, versuchen wir, automatisch Bestellungen aufzugeben, was unsere Erfolgswahrscheinlichkeit erheblich erhöhen kann.

2 Design-Ideen

JDs Eilkauf von Waren ist hauptsächlich in zwei Arten unterteilt:

Terminkauf: zum Kauf vor Ort geöffnet, was dem Bestellvorgang für gewöhnliche Waren entspricht: separate Kaufschnittstelle und Bestellvorgang.

Natürlich ist dieses Mal auf Reservierungen von Eilkäufen oder Bestellungen, die nicht vorrätig sind, ausgerichtet, d Warenkorb→ Wählen Sie das Eilkaufprodukt→ Klicken Sie zur Kasse → Klicken Sie auf „Bestellung abschicken“→

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

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 None

    return 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 False

    respJson = self.parseJson(resp.text)
    if respJson['code'] != 200:
        return None
    else:
        return respJson['ticket']
验证 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 False

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

此时验证 Ticket 有效后使用 pickle

Da der Autor keinen Client hat, der Pakete erfassen kann, habe ich mich entschieden, die WEB-Schnittstelle von JD.com zu verwenden, um unser Skriptprogramm zu implementieren.

Nach der Analyse des Bestellvorgangs auf der Webseite von JD.com haben wir unser Skriptprogramm in vier Module unterteilt:

Konto-Anmeldemodul

,
Bestandsüberwachungsmodul, Warenkorbverwaltungsmodul
,
Auftragsverwaltungsmodul.
3.1 Kontoanmeldung

Aufgrund der Einschränkungen durch Bestätigungscodes bei der Verwendung von Kontokennwörtern scannen Sie den QR-Code zum Anmelden, um diese zu umgehen.

Studenten, die mit der QR-Code-Anmeldung nicht vertraut sind oder daran nicht interessiert sind, können sich Zhou Zhous vorherigen Blogbeitrag zum Prinzip und zur Umsetzung der Scan-QR-Code-Anmeldung ansehen.
Dieses Mal müssen wir nur eine Paketerfassungsanalyse auf der JD-Anmeldeseite durchführen und finden mehrere nützliche Schnittstellen:
Anmelde-QR-Code abrufen
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
Ticket abrufen
def getItemStock(self, skuId, num, areaId):

    item = self.itemDetails.get(skuId)

    if not item:
        return False

    url = '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).text
        respJson = 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)

Ticket überprüfen

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

Ticket überprüfen Zu diesem Zeitpunkt verwenden Sie nach der Validierung die Bibliothek pickle, um die Cookies in der Programmsitzung lokal für die nächste Verwendung zu speichern.

3.2 Bestandsüberwachung
Die Bestandsüberwachung ist relativ einfach. Analysieren Sie die Produktdetailseite, erhalten Sie die Geschäfts-ID und die Produktklassifizierungsattribute:
Produktdetails abrufen
def addCartSku(self, skuId, skuNum):
    """ 加入购入车
    skuId 商品sku
    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'
    }
    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']
🎜Lagerbestand abfragen🎜🎜
def changeCartSkuCount(self, skuId, skuUid, skuNum, areaId):
    """ 修改购物车商品数量
    skuId 商品sku
    skuUid 商品用户关系
    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.3 Warenkorbbedienung 🎜🎜Keine Wir können über den Seitenvorgang keine vorrätigen Produkte zum Warenkorb hinzufügen. Wir können hier andere vorrätige Produkte ausprobieren. Wir überprüfen hauptsächlich die Oberfläche zum Hinzufügen, Löschen, Ändern und Überprüfen: 🎜🎜 🎜Alle ausgewählten Produkte stornieren Um die vorhandenen Daten im Warenkorb Ihres Kontos nicht zu zerstören, verwenden Sie die folgenden Schritte, um den Warenkorb vorzubereiten: 🎜🎜Abbrechen Alle prüfen (zurück zur Warenkorbinformation); ändern Sie die Menge des Produkts, wenn es sich bereits im Warenkorb befindet; Fügen Sie es dem Warenkorb hinzu, wenn es nicht im Warenkorb ist. 3.4 Bestellvorgänge🎜🎜Wenn wir den Warenkorb vorbereiten (die zu kaufenden Artikel auswählen und die Kaufmenge anpassen), können wir mit den nächsten auftragsbezogenen Vorgängen fortfahren: 🎜🎜🎜Rufen Sie das Abrechnungsformular ab🎜🎜
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',
    }
🎜🎜Senden Sie die Bestellung 🎜🎜
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=2018070403091

    data = {
        '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,
    }

Das obige ist der detaillierte Inhalt vonWie schreibe ich mit Python ein automatisches JD-Bestellskript?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen