使用Python開(kāi)發(fā)個(gè)京東上搶口罩的小實(shí)例(僅作技術(shù)研究學(xué)習(xí)使用)
全國(guó)抗”疫”這么久終于見(jiàn)到曙光,在家待了將近一個(gè)月,現(xiàn)在終于可以去上班了,可是卻發(fā)現(xiàn)出門(mén)必備的口罩卻一直買(mǎi)不到。最近看到京東上每天都會(huì)有口罩的秒殺活動(dòng),試了幾次卻怎么也搶不到,到了搶購(gòu)的時(shí)間,瀏覽器的頁(yè)面根本就刷新不出來(lái),等刷出來(lái)秒殺也結(jié)束了?,F(xiàn)在每天只放出一萬(wàn)個(gè),卻有幾百萬(wàn)人在搶?zhuān)芟胫绖e人是怎么搶到的,于是就在網(wǎng)上找了大神公開(kāi)出來(lái)的搶購(gòu)代碼。看了下代碼并不復(fù)雜,現(xiàn)在我們就報(bào)著學(xué)習(xí)的態(tài)度一起看看。
使用模塊
requests:類(lèi)似 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)物車(chē)
remove_item() # 刪除購(gòu)物車(chē)
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ǔ)上增加了些注釋?zhuān)梢宰屛覀兏菀酌靼状a的含義。下面我們就選擇幾個(gè)比較關(guā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)物車(chē)
接下來(lái)我們?cè)倏聪氯绻砑由唐返劫?gòu)物車(chē)的,代碼如下:
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)物車(chē)后直接跳轉(zhuǎn)到購(gòu)物車(chē)頁(yè)面
if 'https://cart.jd.com/cart.action' in resp.url:
result = True
else:
# 普通商品成功加入購(gòu)物車(chē)后會(huì)跳轉(zhuǎn)到提示 "商品已成功加入購(gòu)物車(chē)!" 頁(yè)面
soup = BeautifulSoup(resp.text, "html.parser")
result = bool(soup.select('h3.ftx-02')) # [<h3 class="ftx-02">商品已成功加入購(gòu)物車(chē)!</h3>]
if result:
logger.info('%s 已成功加入購(gòu)物車(chē)', sku_id)
else:
logger.error('%s 添加到購(gòu)物車(chē)失敗', sku_id)
在這里,只是簡(jiǎn)單幾行代碼就能將端口添加到購(gòu)物車(chē)了,而且這里還區(qū)分了不同類(lèi)型商品添加到購(gòu)物車(chē)返回的頁(yè)面結(jié)果是不同的,所以要進(jìn)行區(qū)別處理。
購(gòu)買(mǎi)商品
將商品添加到購(gòu)物車(chē)了,接下來(lái)我們就得提交結(jié)算頁(yè)了,也就是將商品提交到付款頁(yè)面,這段代碼有點(diǎn)多,我簡(jiǎn)化了下并加了些注釋?zhuān)?/p>
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)物車(chē)為空 或 未勾選購(gòu)物車(chē)中商品)'
elif result_code == 60123:
resultMessage = resultMessage + '(需要在payment_pwd參數(shù)配置支付密碼)'
elif result_code == 60070:
resultMessage = resultMessage + '(省份不支持銷(xiāo)售)'
skuids.remove(sku_id)
logger.info('[%s]類(lèi)型口罩不支持銷(xiāo)售', 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)物車(chē)要復(fù)雜,果然跟錢(qián)有關(guān)的都不簡(jiǎn)單。好了,到了結(jié)算頁(yè)面剩下就是付款了,這個(gè)就不需要再搶了,畢竟也沒(méi)人會(huì)搶著給你付錢(qián)的。
好了本文主要講的使用Python開(kāi)發(fā)個(gè)京東上搶口罩的小實(shí)例只作技術(shù)研究學(xué)習(xí)使用,請(qǐng)不要胡亂使用。更多關(guān)于Python模塊requests,beautifulsoup4,pillow使用方法請(qǐng)查看下面的相關(guān)鏈接
- 基于Python爬取京東雙十一商品價(jià)格曲線
- Python CSS選擇器爬取京東網(wǎng)商品信息過(guò)程解析
- Python利用Xpath選擇器爬取京東網(wǎng)商品信息
- Python基于BeautifulSoup爬取京東商品信息
- 使用 Python 在京東上搶口罩的思路詳解
- springboot使用@value讀取配置的方法
- Python實(shí)現(xiàn)京東秒殺功能代碼
- Python爬蟲(chóng)實(shí)現(xiàn)抓取京東店鋪信息及下載圖片功能示例
- python實(shí)現(xiàn)京東秒殺功能
- Python實(shí)現(xiàn)自動(dòng)上京東搶手機(jī)
- python 爬蟲(chóng)爬取京東ps4售賣(mài)情況
相關(guān)文章
淺談Django QuerySet對(duì)象(模型.objects)的常用方法
這篇文章主要介紹了淺談Django QuerySet對(duì)象(模型.objects)的常用方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
python Selenium爬取內(nèi)容并存儲(chǔ)至MySQL數(shù)據(jù)庫(kù)的實(shí)現(xiàn)代碼
這篇文章主要介紹了python Selenium爬取內(nèi)容并存儲(chǔ)至MySQL數(shù)據(jù)庫(kù)的實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-03-03
python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5 UI主線程與耗時(shí)線程分離詳細(xì)方法實(shí)例
這篇文章主要介紹了python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5 UI主線程與耗時(shí)線程分離詳細(xì)方法實(shí)例,需要的朋友可以參考下2020-02-02
Python GUI編程學(xué)習(xí)筆記之tkinter控件的介紹及基本使用方法詳解
這篇文章主要介紹了Python GUI編程學(xué)習(xí)筆記之tkinter控件的介紹及基本使用方法,結(jié)合實(shí)例形式詳細(xì)分析了Python GUI編程中tkinter控件的原理、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-03-03

