您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么使用Python編寫(xiě)自動(dòng)搶購(gòu)京東下單腳本”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么使用Python編寫(xiě)自動(dòng)搶購(gòu)京東下單腳本”吧!
經(jīng)過(guò)無(wú)數(shù)次搶購(gòu)失敗后,發(fā)現(xiàn)商家會(huì)不定時(shí)的放出少量貨源,目測(cè)每次會(huì)有幾臺(tái)。如果我們編寫(xiě)一個(gè)腳本程序24小時(shí)不間斷監(jiān)聽(tīng)商品庫(kù)存,一旦查詢到貨源便開(kāi)始嘗試自動(dòng)下單,這樣就可以極大提高我們的成功概率。
京東對(duì)于商品的搶購(gòu)主要分為兩種:
預(yù)約搶購(gòu):到點(diǎn)開(kāi)放購(gòu)買,和普通商品下單流程一致;秒殺商品:?jiǎn)为?dú)的搶購(gòu)接口和下單流程。
當(dāng)然本次針對(duì)的預(yù)約搶購(gòu)類或無(wú)貨訂購(gòu)類,即整體下單流程和購(gòu)買普通商品時(shí)一樣:
登錄賬號(hào) → 進(jìn)入購(gòu)物車 → 選擇搶購(gòu)商品 → 點(diǎn)擊去結(jié)算 → 點(diǎn)擊提交訂單 → 選擇付款方式并付款
。
由于筆者本人沒(méi)有一個(gè)可以抓包的客戶端,決定采用京東 WEB 端接口實(shí)現(xiàn)我們的腳本程序。
于是經(jīng)過(guò)對(duì)京東網(wǎng)頁(yè)下單流程的分析,將我們的腳本程序分為四個(gè)模塊:賬號(hào)登錄模塊、庫(kù)存監(jiān)聽(tīng)模塊、購(gòu)物車管理模塊、訂單管理模塊。
由于使用賬號(hào)密碼時(shí)有驗(yàn)證碼限制,此處采用掃碼登錄方式繞過(guò)。
如對(duì)掃碼登錄不熟悉或感興趣的同學(xué)可以查看周周之前的博文 掃碼登錄原理和實(shí)現(xiàn)。
本次只要針對(duì)京東登錄頁(yè)進(jìn)行抓包分析,找到幾個(gè)有用接口:
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
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']
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']
此時(shí)驗(yàn)證 Ticket 有效后使用 pickle
庫(kù)將程序會(huì)話中的 cookie 保存到本地以便下次使用。
庫(kù)存監(jiān)聽(tīng)較為簡(jiǎn)單,分析商品詳情頁(yè),獲取店鋪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('獲取商品信息失敗,請(qǐng)檢查SKU是否正確') detail = dict(catId=cat, venderId=vender) return detail
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') # 商品是否上架 # 商品庫(kù)存狀態(tài):33 -- 現(xiàn)貨 0,34 -- 無(wú)貨 36 -- 采購(gòu)中 40 -- 可配貨 stockState = stockInfo.get('StockState') return skuState == 1 and stockState in (33, 40)
無(wú)貨商品加入到購(gòu)物車我們是無(wú)法通過(guò)頁(yè)面操作的,我們這邊可以使用其他有貨商品進(jìn)行嘗試,主要查看購(gòu)物車的增刪改查接口:
def uncheckCartAll(self): """ 取消所有選中商品 return 購(gòu)物車信息 """ 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): """ 加入購(gòu)入車 skuId 商品sku skuNum 購(gòu)買數(shù)量 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): """ 修改購(gòu)物車商品數(shù)量 skuId 商品sku skuUid 商品用戶關(guān)系 skuNum 購(gòu)買數(shù)量 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']
以上是我們一次購(gòu)買需要用到的最少接口,為了不破壞賬戶購(gòu)物車中已有數(shù)據(jù),采用一下步驟準(zhǔn)備好購(gòu)物車:
取消全部勾選(返回購(gòu)物車信息);已在購(gòu)物車則修改商品數(shù)量;不在購(gòu)物車則加入購(gòu)物車。 3.4 訂單操作
當(dāng)我們準(zhǔn)備好購(gòu)物車之后(選中購(gòu)買商品以及調(diào)整購(gòu)買數(shù)量),就可以進(jìn)行下一步訂單相關(guān)操作:
def getCheckoutPage(self): """獲取訂單結(jié)算頁(yè)面信息 :return: 結(jié)算信息 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 訂單提交結(jié)果 """ 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, }
到此,相信大家對(duì)“怎么使用Python編寫(xiě)自動(dòng)搶購(gòu)京東下單腳本”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。