Skip to content

Commit

Permalink
验证码服务更新
Browse files Browse the repository at this point in the history
  • Loading branch information
cycz committed Feb 9, 2020
1 parent ad8c8b9 commit 2ec608c
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 85 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@

**注意--极速模式默认清空购物车**

正常模式下单流程
正常模式下单流程(1.7毫秒左右)
- [x] 检测有货
- [x] 检测下柜
- [x] 加入购物车
- [x] 查看购物车
- [x] 下单

极速模式下单流程
极速模式下单流程(1.4秒左右)
- [x] 检测有货
- [x] 加入购物车
- [x] 下单
Expand All @@ -40,7 +40,7 @@

**注意--V3版本默认清空购物车**

V3版本下单流程
V3版本下单流程(1秒左右)
- [x] 提前加入购物车
- [x] 检测有货
- [x] 下单
Expand All @@ -64,6 +64,7 @@ V3
- [x] 邮件、微信通知

## 更新记录
- 【2020.02.09】部分下单需要验证码识别问题,部分bug优化。
- 【2020.02.08】V2版本,区分下单模式,config中错别字,bug修复。
- 【2020.02.07】V3版本,减少提交订单的请求量,总而言之就是更快(只能监控一件商品)。
- 【2020.02.07】无货等情况下单失败不重试。
Expand Down
17 changes: 12 additions & 5 deletions configDemo.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,22 @@ sc_key = test
# 如果没有上述情况,下方请留空。
payment_pwd =
#暂时不填写
eid =
fp =


[V2]
# skuids 英文逗号[,]相隔 (分清楚英文和中文逗号) 末尾不要带逗号
skuids = 65466451629,65437208345,7498169,7498165,7263128,7498167,17449572304,37934196731,100001086804,56657322838,56657322841,100005294853,1938795,15595191653,15595191654,45923412989
skuids = 3088512,65437208345,7498169,7498165,7263128,7498167,17449572304,37934196731,100001086804,56657322838,56657322841,100005294853,1938795,15595191653,15595191654,45923412989
# V2版本下单速度区分 极速模式 【1】 和 正常模式 【2】(极速模式缩短不必要的流程)
model = 2
model = 1

[V3]
# v3版本的skuid,最多写一件
skuid = 65466451629
skuid = 3088512


[Temporary]
#一般不需要修改
eid =
fp =
# 打码服务器
captchaUrl = http://122.51.18.81:8111/pic
139 changes: 106 additions & 33 deletions jdBuyMask_V2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
京东抢购口罩程序
通过商品的skuid、地区id抢购
'''
from io import BytesIO
import requests
import time
import json
Expand All @@ -13,7 +14,7 @@
from config import global_config
from message import message
import traceback

from PIL import Image
'''
需要修改
'''
Expand All @@ -33,7 +34,8 @@
# 商品id
skuidsString = global_config.getRaw('V2', 'skuids')
skuids = str(skuidsString).split(',')

# 验证码服务地址
captchaUrl = global_config.getRaw('Temporary', 'captchaUrl')
if not modelType:
logger.error('请在configDemo.ini文件填写下单model')

Expand All @@ -47,11 +49,15 @@
备用
'''
# eid
eid = global_config.getRaw('config', 'eid')
fp = global_config.getRaw('config', 'fp')
eid = global_config.getRaw('Temporary', 'eid')
fp = global_config.getRaw('Temporary', 'fp')
# 支付密码
payment_pwd = global_config.getRaw('config', 'payment_pwd')

is_Submit_captcha = False
submit_captcha_rid = ''
submit_captcha_text = ''
encryptClientInfo = ''
submit_Time = 0
session = requests.session()
session.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",
Expand Down Expand Up @@ -106,6 +112,8 @@ def validate_cookies():
logger.info('第【%s】次请重新获取cookie', flag)
time.sleep(5)
continue
message.sendAny('脚本登录cookie失效了,请重新登录')
sys.exit(1)


def getUsername():
Expand Down Expand Up @@ -288,6 +296,15 @@ def get_checkout_page_detail():
return '刷新太频繁了'
soup = BeautifulSoup(resp.text, "html.parser")
risk_control = get_tag_value(soup.select('input#riskControl'), 'value')
showCheckCode = get_tag_value(soup.select('input#showCheckCode'), 'value')
if not showCheckCode:
pass
else:
if showCheckCode == 'true':
logger.info('提交订单需要验证码')
global is_Submit_captcha, encryptClientInfo
encryptClientInfo = get_tag_value(soup.select('input#encryptClientInfo'), 'value')
is_Submit_captcha = True

order_detail = {
'address': soup.find('span', id='sendAddr').text[5:], # remove '寄送至: ' from the begin
Expand All @@ -297,12 +314,12 @@ def get_checkout_page_detail():
}

logger.info("下单信息:%s", order_detail)
# return order_detail
return risk_control
except requests.exceptions.RequestException as e:
logger.error('订单结算页面获取异常:%s' % e)
except Exception as e:
logger.error('下单页面数据解析异常:%s', e)
return risk_control
return ''


def submit_order(risk_control, sku_id):
Expand All @@ -317,17 +334,6 @@ def submit_order(risk_control, sku_id):
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

# overseaPurchaseCookies:
# vendorRemarks: []
# submitOrderParam.sopNotPutInvoice: false
# submitOrderParam.trackID: TestTrackId
# submitOrderParam.ignorePriceChange: 0
# submitOrderParam.btSupport: 0
# riskControl:
# submitOrderParam.isBestCoupon: 1
# submitOrderParam.jxj: 1
# submitOrderParam.trackId:

data = {
'overseaPurchaseCookies': '',
'vendorRemarks': '[]',
Expand Down Expand Up @@ -357,12 +363,21 @@ def encrypt_payment_pwd(payment_pwd):
"Connection": "keep-alive",
'Host': 'trade.jd.com',
}
for count in range(1, 2):
for count in range(1, 3):
logger.info('第[%s/%s]次尝试提交订单', count, 3)
try:
if is_Submit_captcha:
global encryptClientInfo, submit_captcha_text, submit_captcha_rid
captcha_result = page_detail_captcha(encryptClientInfo)
# 验证码服务错误
if not captcha_result:
logger.error('验证码服务异常')
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('本次提交订单耗时[%s]毫秒',str(int(time.time()*1000)-submit_Time))
# 返回信息示例:
# 下单失败
# {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60123, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '请输入支付密码!'}
Expand All @@ -373,7 +388,6 @@ def encrypt_payment_pwd(payment_pwd):
# {"orderXml":null,"cartXml":null,"noStockSkuIds":"","reqInfo":null,"hasJxj":false,"overSea":false,"addedServiceList":null,"sign":null,"pin":null,"needCheckCode":true,"success":false,"resultCode":0,"orderId":0,"submitSkuNum":0,"deductMoneyFlag":0,"goJumpOrderCenter":false,"payInfo":null,"scaleSkuInfoListVO":null,"purchaseSkuInfoListVO":null,"noSupportHomeServiceSkuList":null,"msgMobile":null,"addressVO":null,"msgUuid":null,"message":"验证码不正确,请重新填写"}
# 下单成功
# {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': True, 'resultCode': 0, 'orderId': 8740xxxxx, 'submitSkuNum': 1, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': None}

if resp_json.get('success'):
logger.info('订单提交成功! 订单号:%s', resp_json.get('orderId'))
return True
Expand All @@ -383,11 +397,8 @@ def encrypt_payment_pwd(payment_pwd):
# self._save_invoice()
if '验证码不正确' in resultMessage:
resultMessage = resultMessage + '(验证码错误)'
message.send('账号订单提交失败,需要验证码。避免死循环退出程序', False)
logger.info('账号订单提交失败,需要验证码。避免死循环退出程序')
sys.exit(1)
# skuids.remove(sku_id)
# logger.info('订单提交失败,避免死循环去除异常id[%s]', sku_id)
logger.info('提交订单验证码[错误]')
continue
else:
resultMessage = resultMessage + '(下单商品可能为第三方商品,将切换为普通发票进行尝试)'
elif result_code == 60077:
Expand All @@ -404,8 +415,65 @@ def encrypt_payment_pwd(payment_pwd):


'''
订单页面验证码
'''


def page_detail_captcha(isId):
url = 'https://captcha.jd.com/verify/image'
acid = '{}_{}'.format(random.random(), random.random())
payload = {
'acid': acid,
'srcid': 'trackWeb',
'is': isId,
}
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": "https://trade.jd.com/shopping/order/getOrderInfo.action",
"Connection": "keep-alive",
'Host': 'captcha.jd.com',
}
try:
resp = session.get(url=url, params=payload, headers=headers)
if not response_status(resp):
logger.error('获取订单验证码失败')
return ''
logger.info('解析验证码开始')
image = Image.open(BytesIO(resp.content))
image.save('captcha.jpg')
result = analysis_captcha(resp.content)
if not result:
logger.error('解析订单验证码失败')
return ''
global submit_captcha_text, submit_captcha_rid
submit_captcha_text = result
submit_captcha_rid = acid
return result
except Exception as e:
logger.error('订单验证码获取异常:%s', e)
return ''


def analysis_captcha(pic):
for i in range(1, 10):
try:
url = captchaUrl
resp = session.post(url, pic)
if not response_status(resp):
logger.error('解析验证码失败')
continue
logger.info('解析验证码[%s]', resp.text)
return resp.text
except Exception as e:
print(traceback.format_exc())
continue
return ''


'''
下柜商品检测
'''


def item_removed(sku_id):
Expand All @@ -425,8 +493,6 @@ def item_removed(sku_id):
购买环节
测试三次
'''


def normalModeBuyMask(sku_id):
cancel_select_all_cart_item()
cart = cart_detail()
Expand Down Expand Up @@ -491,6 +557,7 @@ def check_stock():
inStockSkuid = []
nohasSkuid = []
abnormalSkuid = []
time.time()
for i in skuids:
try:
if respjson[i]['StockStateName'] != '无货':
Expand Down Expand Up @@ -555,6 +622,8 @@ def select_all_cart_item():
def normalModeAutoBuy(inStockSkuid):
for skuId in inStockSkuid:
if item_removed(skuId):
global submit_Time
submit_Time = int(time.time() * 1000)
logger.info('[%s]类型口罩有货啦!马上下单', skuId)
skuidUrl = 'https://item.jd.com/' + skuId + '.html'
if normalModeBuyMask(skuId):
Expand All @@ -568,6 +637,8 @@ def normalModeAutoBuy(inStockSkuid):

def fastModeAutoBuy(inStockSkuid):
for skuId in inStockSkuid:
global submit_Time
submit_Time = int(time.time()*1000)
logger.info('[%s]类型口罩有货啦!马上下单', skuId)
skuidUrl = 'https://item.jd.com/' + skuId + '.html'
if fastModeBuyMask(skuId):
Expand All @@ -590,21 +661,21 @@ def normalMode():
if flag == 1:
validate_cookies()
getUsername()

# modelType
logger.info('第' + str(flag) + '次 ')
flag += 1
# 检测库存
inStockSkuid = check_stock()
# 下单任务
normalModeAutoBuy(inStockSkuid)
# 休眠模块
timesleep = random.randint(5, 10) / 10
time.sleep(timesleep)
# 校验是否还在登录模块
if flag % 40 == 0:
logger.info('校验是否还在登录')
validate_cookies()
except Exception as e:

print(traceback.format_exc())
time.sleep(10)

Expand All @@ -625,18 +696,20 @@ def fastMode():
inStockSkuid = check_stock()
# 下单任务
fastModeAutoBuy(inStockSkuid)
# 休眠模块
timesleep = random.randint(5, 10) / 10
time.sleep(timesleep)
if flag % 40 == 0:
# 校验是否还在登录模块
if flag % 60 == 0:
logger.info('校验是否还在登录')
validate_cookies()
except Exception as e:

print(traceback.format_exc())
time.sleep(10)


if modelType == '2':
logger.info('V2版本当前模式[普通模式]')
normalMode()
elif modelType == '1':
logger.info('V2版本当前模式[极速模式]')
fastMode()
Loading

0 comments on commit 2ec608c

Please sign in to comment.