<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
經過無數次搶購失敗後,發現商家會不定時的放出少量貨源,目測每次會有幾臺。如果我們編寫一個指令碼程式24小時不間斷監聽商品庫存,一旦查詢到貨源便開始嘗試自動下單,這樣就可以極大提高我們的成功概率。
京東對於商品的搶購主要分為兩種:
預約搶購:到點開放購買,和普通商品下單流程一致;秒殺商品:單獨的搶購介面和下單流程。
當然本次針對的預約搶購類或無貨訂購類,即整體下單流程和購買普通商品時一樣:
登入賬號 → 進入購物車 → 選擇搶購商品 → 點選去結算 → 點選提交訂單 → 選擇付款方式並付款
。
由於筆者本人沒有一個可以抓包的使用者端,決定採用京東 WEB 端介面實現我們的指令碼程式。
於是經過對京東網頁下單流程的分析,將我們的指令碼程式分為四個模組:賬號登入模組、庫存監聽模組、購物車管理模組、訂單管理模組。
由於使用賬號密碼時有驗證碼限制,此處採用掃碼登入方式繞過。
如對掃碼登入不熟悉或感興趣的同學可以檢視週週之前的博文 掃碼登入原理和實現。
本次只要針對京東登入頁進行抓包分析,找到幾個有用介面:
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']
此時驗證 Ticket 有效後使用 pickle
庫將程式對談中的 cookie 儲存到本地以便下次使用。
庫存監聽較為簡單,分析商品詳情頁,獲取店鋪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 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)
無貨商品加入到購物車我們是無法通過頁面操作的,我們這邊可以使用其他有貨商品進行嘗試,主要檢視購物車的增刪改查介面:
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 商品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']
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.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=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, }
專案完整原始碼請看:https://github.com/zas023/JdBuyer
目前完成度較高,可以直接使用:
並提供 Windows 和 macOS 兩種使用者端;支援下單後微信通知觸達。
指令碼自動化操作確實可以實現搶購商品,相比手動操作有較高的下單成功率,但僅靠上述程式碼想與某些專業搶購的伺服器進行比較還是相去甚遠。
到此這篇關於教你用Python寫一個京東自動下單搶購指令碼的文章就介紹到這了,更多相關Python京東自動搶購指令碼內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45