使用 Python 在京東上搶口罩的思路詳解
全國(guó)抗"疫"這么久終于見(jiàn)到曙光,在家待了將近一個(gè)月,現(xiàn)在終于可以去上班了,可是卻發(fā)現(xiàn)出門必備的口罩卻一直買不到。最近看到京東上每天都會(huì)有口罩的秒殺活動(dòng),試了幾次卻怎么也搶不到,到了搶購(gòu)的時(shí)間,瀏覽器的頁(yè)面根本就刷新不出來(lái),等刷出來(lái)秒殺也結(jié)束了。現(xiàn)在每天只放出一萬(wàn)個(gè),卻有幾百萬(wàn)人在搶,很想知道別人是怎么搶到的,于是就在網(wǎng)上找了大神公開(kāi)出來(lái)的搶購(gòu)代碼。看了下代碼并不復(fù)雜,現(xiàn)在我們就報(bào)著學(xué)習(xí)的態(tài)度一起看看。
使用模塊
首先打開(kāi)項(xiàng)目中 requirements.txt 文件,看下它都需要哪些模塊:
- requests:類似 urllib ,主要用于向網(wǎng)站發(fā)送 HTTP 請(qǐng)求。
- beautifulsoup4: HTML 解析器,用于將 HTML 文檔轉(zhuǎn)換成一個(gè)復(fù)雜的樹(shù)形結(jié)構(gòu)。
- pillow:Python 圖像處理標(biāo)準(zhǔn)庫(kù),用于識(shí)別驗(yàn)證碼。
配置文件
一般項(xiàng)目中我們都需要把一些可配置的內(nèi)容放到配置文件中,現(xiàn)在我們來(lái)看下這里主要配置項(xiàng):
# 郵寄地所屬地區(qū)ID area = 123456 # 這是配置的商品的ID skuid = 6828101 # 打碼服務(wù)器的地址 captchaUrl = http://xxx/pic # 通知郵箱 mail = xxxxxx@qq.com # cookie的設(shè)置 cookies_String = shshshfpa21jsda8923892949204923123 OK,有了配置文件,那我們就得有一段讀取配置文件的代碼,這段代碼實(shí)現(xiàn)將配置內(nèi)容加載到內(nèi)存中。 import os import configparser # 加載配置文件 class Config(object): def __init__(self, config_file='configDemo.ini'): self._path = os.path.join(os.getcwd(), config_file) if not os.path.exists(self._path): raise FileNotFoundError("No such file: config.ini") self._config = configparser.ConfigParser() self._config.read(self._path, encoding='utf-8-sig') self._configRaw = configparser.RawConfigParser() self._configRaw.read(self._path, encoding='utf-8-sig') def get(self, section, name): return self._config.get(section, name) def getRaw(self, section, name): return self._configRaw.get(section, name)
主程序模塊
我看 GitHub 上也有實(shí)現(xiàn)了運(yùn)行程序后通過(guò)京東 App 掃碼登陸,然后再通過(guò)登陸 Cookie 訪問(wèn)網(wǎng)站的,不過(guò)這里并沒(méi)有使用這種方式,畢竟我們打開(kāi)瀏覽器開(kāi)發(fā)者工具也能很容易獲取到登陸的 Cookie ,這里就是將 Cookie 直接放到配置文件里的方式。
# 主程序入口 # 檢查是否存在要搶購(gòu)的端口,然后進(jìn)入循環(huán)掃描 if len(skuids) != 1: logger.info('請(qǐng)準(zhǔn)備一件商品') skuId = skuids[0] flag = 1 # 循環(huán)掃描該商品是否有貨,有庫(kù)存即會(huì)自動(dòng)下單,無(wú)庫(kù)存則休眠后繼續(xù)掃描 while (1): try: # 初始化校驗(yàn) if flag == 1: logger.info('當(dāng)前是V3版本') validate_cookies() # 校驗(yàn)登陸狀態(tài) getUsername() # 獲取登陸用戶信息 select_all_cart_item() # 全選購(gòu)物車 remove_item() # 刪除購(gòu)物車 add_item_to_cart(skuId) # 增加搶購(gòu)的商品 # 檢測(cè)配置文件修改 if int(time.time()) - configTime >= 60: check_Config() logger.info('第' + str(flag) + '次 ') # 計(jì)數(shù)器 flag += 1 # 檢查庫(kù)存模塊 inStockSkuid = check_stock(checksession, skuids, area) # 自動(dòng)下單模塊 V3AutoBuy(inStockSkuid) # 休眠模塊 timesleep = random.randint(1, 3) / 10 time.sleep(timesleep) # 校驗(yàn)是否還在登錄模塊 if flag % 100 == 0: V3check(skuId) except Exception as e: print(traceback.format_exc()) time.sleep(10)
以上就是該項(xiàng)目主程序,我已經(jīng)將代碼在原來(lái)基礎(chǔ)上增加了些注釋,可以讓我們更容易明白代碼的含義。下面我們就選擇幾個(gè)比較關(guān)鍵的代碼分析一下。
登陸狀態(tài)校驗(yàn)
# 校驗(yàn)登陸狀態(tài) def validate_cookies(): for flag in range(1, 3): try: targetURL = 'https://order.jd.com/center/list.action' payload = { 'rid': str(int(time.time() * 1000)), } resp = session.get(url=targetURL, params=payload, allow_redirects=False) if resp.status_code == requests.codes.OK: logger.info('登錄成功') return True else: logger.info('第【%s】次請(qǐng)重新獲取cookie', flag) time.sleep(5) continue except Exception as e: logger.info('第【%s】次請(qǐng)重新獲取cookie', flag) time.sleep(5) continue message.sendAny('腳本登錄cookie失效了,請(qǐng)重新登錄') sys.exit(1)
以上代碼是每次調(diào)用時(shí),循環(huán)兩次獲取通過(guò) session 獲取當(dāng)前登陸狀態(tài),如果兩次后依然失敗則退出程序。
添加商品到購(gòu)物車
接下來(lái)我們?cè)倏聪氯绻砑由唐返劫?gòu)物車的,代碼如下:
def add_item_to_cart(sku_id): # 請(qǐng)求添加商品url url = 'https://cart.jd.com/gate.action' payload = { 'pid': sku_id, 'pcount': 1, 'ptype': 1, } # 返回結(jié)果 resp = session.get(url=url, params=payload) # 套裝商品加入購(gòu)物車后直接跳轉(zhuǎn)到購(gòu)物車頁(yè)面 if 'https://cart.jd.com/cart.action' in resp.url: result = True else: # 普通商品成功加入購(gòu)物車后會(huì)跳轉(zhuǎn)到提示 "商品已成功加入購(gòu)物車!" 頁(yè)面 soup = BeautifulSoup(resp.text, "html.parser") result = bool(soup.select('h3.ftx-02')) # [<h3 class="ftx-02">商品已成功加入購(gòu)物車!</h3>] if result: logger.info('%s 已成功加入購(gòu)物車', sku_id) else: logger.error('%s 添加到購(gòu)物車失敗', sku_id)
在這里,只是簡(jiǎn)單幾行代碼就能將端口添加到購(gòu)物車了,而且這里還區(qū)分了不同類型商品添加到購(gòu)物車返回的頁(yè)面結(jié)果是不同的,所以要進(jìn)行區(qū)別處理。
購(gòu)買商品
將商品添加到購(gòu)物車了,接下來(lái)我們就得提交結(jié)算頁(yè)了,也就是將商品提交到付款頁(yè)面,這段代碼有點(diǎn)多,我簡(jiǎn)化了下并加了些注釋:
def submit_order(session, risk_control, sku_id, skuids, submit_Time, encryptClientInfo, is_Submit_captcha, payment_pwd, submit_captcha_text, submit_captcha_rid): # 提交端口的url url = 'https://trade.jd.com/shopping/order/submitOrder.action' # 提交參數(shù) data = { 'overseaPurchaseCookies': '', 'vendorRemarks': '[]', 'submitOrderParam.sopNotPutInvoice': 'false', 'submitOrderParam.trackID': 'TestTrackId', 'submitOrderParam.ignorePriceChange': '0', 'submitOrderParam.btSupport': '0', 'riskControl': risk_control, 'submitOrderParam.isBestCoupon': 1, 'submitOrderParam.jxj': 1, 'submitOrderParam.trackId': '9643cbd55bbbe103eef18a213e069eb0', # Todo: need to get trackId 'submitOrderParam.needCheck': 1, } # 如果用到京豆會(huì)需要輸入支付密碼 def encrypt_payment_pwd(payment_pwd): return ''.join(['u3' + x for x in payment_pwd]) # 校驗(yàn)支付密碼 if len(payment_pwd) > 0: data['submitOrderParam.payPassword'] = encrypt_payment_pwd(payment_pwd) # 請(qǐng)求報(bào)文頭 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", "Referer": "http://trade.jd.com/shopping/order/getOrderInfo.action", "Connection": "keep-alive", 'Host': 'trade.jd.com', } # 訂單提交會(huì)嘗試兩次 for count in range(1, 3): logger.info('第[%s/%s]次嘗試提交訂單', count, 3) try: # 可能會(huì)存在的校驗(yàn)碼 if is_Submit_captcha: captcha_result = page_detail_captcha(session, encryptClientInfo) # 驗(yàn)證碼服務(wù)錯(cuò)誤 if not captcha_result: logger.error('驗(yàn)證碼服務(wù)異常') continue data['submitOrderParam.checkcodeTxt'] = submit_captcha_text data['submitOrderParam.checkCodeRid'] = submit_captcha_rid # 提交訂單 resp = session.post(url=url, data=data, headers=headers) resp_json = json.loads(resp.text) logger.info('本次提交訂單耗時(shí)[%s]毫秒', str(int(time.time() * 1000) - submit_Time)) # 判斷是否提交成功 if resp_json.get('success'): logger.info('訂單提交成功! 訂單號(hào):%s', resp_json.get('orderId')) return True else: # 提交失敗返回的多種原因 resultMessage, result_code = resp_json.get('message'), resp_json.get('resultCode') if result_code == 0: # self._save_invoice() if '驗(yàn)證碼不正確' in resultMessage: resultMessage = resultMessage + '(驗(yàn)證碼錯(cuò)誤)' logger.info('提交訂單驗(yàn)證碼[錯(cuò)誤]') continue else: resultMessage = resultMessage + '(下單商品可能為第三方商品,將切換為普通發(fā)票進(jìn)行嘗試)' elif result_code == 60077: resultMessage = resultMessage + '(可能是購(gòu)物車為空 或 未勾選購(gòu)物車中商品)' elif result_code == 60123: resultMessage = resultMessage + '(需要在payment_pwd參數(shù)配置支付密碼)' elif result_code == 60070: resultMessage = resultMessage + '(省份不支持銷售)' skuids.remove(sku_id) logger.info('[%s]類型口罩不支持銷售', sku_id) logger.info('訂單提交失敗, 錯(cuò)誤碼:%s, 返回信息:%s', result_code, resultMessage) logger.info(resp_json) return False except Exception as e: print(traceback.format_exc()) continue
以上代碼實(shí)現(xiàn)了商品自動(dòng)提交到結(jié)算頁(yè)面,這段明顯比添加購(gòu)物車要復(fù)雜,果然跟錢有關(guān)的都不簡(jiǎn)單。好了,到了結(jié)算頁(yè)面剩下就是付款了,這個(gè)就不需要再搶了,畢竟也沒(méi)人會(huì)搶著給你付錢的。
總結(jié)
本文為大家介紹了一個(gè)京東搶購(gòu)的小工具,它實(shí)現(xiàn)了掃描是否有庫(kù)存,發(fā)現(xiàn)有庫(kù)存就自動(dòng)下單,并且可以自動(dòng)提交到結(jié)算頁(yè)面。而它所實(shí)現(xiàn)方式也并不算太復(fù)雜,進(jìn)一步分析了它的部分代碼,有興趣的小伙伴可以去文末 GitHub 項(xiàng)目網(wǎng)址上了解更多,再次感謝開(kāi)發(fā)者的付出和分享。
參考
GitHub項(xiàng)目網(wǎng)址:https://github.com/cycz/jdBuyMask
到此這篇關(guān)于使用 Python 在京東上搶口罩的思路詳解的文章就介紹到這了,更多相關(guān)Python 京東搶口罩內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- python使用Word2Vec進(jìn)行情感分析解析
- Python實(shí)現(xiàn)購(gòu)物評(píng)論文本情感分析操作【基于中文文本挖掘庫(kù)snownlp】
- python snownlp情感分析簡(jiǎn)易demo(分享)
- Python 京東云無(wú)線寶消息推送功能
- python基于scrapy爬取京東筆記本電腦數(shù)據(jù)并進(jìn)行簡(jiǎn)單處理和分析
- python pyppeteer 破解京東滑塊功能的代碼
- Python實(shí)現(xiàn)京東搶秒殺功能
- Python之京東商品秒殺的實(shí)現(xiàn)示例
- python 爬蟲(chóng)爬取京東ps4售賣情況
- Python基于BeautifulSoup爬取京東商品信息
- springboot使用@value讀取配置的方法
- 基于Python+Appium實(shí)現(xiàn)京東雙十一自動(dòng)領(lǐng)金幣功能
- python 爬取京東指定商品評(píng)論并進(jìn)行情感分析
相關(guān)文章
Python實(shí)現(xiàn)OCR識(shí)別之pytesseract案例詳解
這篇文章主要介紹了Python實(shí)現(xiàn)OCR識(shí)別之pytesseract案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07Python通過(guò)串口實(shí)現(xiàn)收發(fā)文件
這篇文章主要為大家詳細(xì)介紹了Python如何通過(guò)串口實(shí)現(xiàn)收發(fā)文件功能,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11Python實(shí)現(xiàn)簡(jiǎn)單多線程任務(wù)隊(duì)列
本文給大家介紹的是使用很簡(jiǎn)單的代碼實(shí)現(xiàn)的多線程任務(wù)隊(duì)列,給大家一個(gè)思路,希望對(duì)大家學(xué)習(xí)python能夠有所幫助2016-02-02Python自動(dòng)化開(kāi)發(fā)學(xué)習(xí)之三級(jí)菜單制作
這篇文章主要為大家詳細(xì)介紹了Python自動(dòng)化開(kāi)發(fā)學(xué)習(xí)之三級(jí)菜單的制作方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Python并發(fā)請(qǐng)求下限制QPS(每秒查詢率)的實(shí)現(xiàn)代碼
這篇文章主要介紹了Python并發(fā)請(qǐng)求下限制QPS(每秒查詢率)實(shí)現(xiàn)方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06基于python實(shí)現(xiàn)開(kāi)箱即用的桌面時(shí)鐘
這篇文章主要為大家詳細(xì)介紹了如何基于python實(shí)現(xiàn)開(kāi)箱一個(gè)即用的桌面時(shí)鐘,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以參考下2023-12-12Python在centos7.6上安裝python3.9的詳細(xì)教程(默認(rèn)python版本為2.7.5)
這篇文章主要介紹了Python在centos7.6上安裝python3.9(默認(rèn)python版本為2.7.5)的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-10-10