Skip to content

Commit

Permalink
Refactor code
Browse files Browse the repository at this point in the history
  • Loading branch information
tychxn committed Jan 15, 2020
1 parent 1697f3d commit f4e8986
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 70 deletions.
159 changes: 89 additions & 70 deletions jd_assistant.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import os
import pickle
import re
import random
import time

import requests
import pickle
from bs4 import BeautifulSoup

from util import *
from timer import Timer
from config import global_config
from timer import Timer
from util import (
USER_AGENT,
DEFAULT_FP,
DEFAULT_EID,
DEFAULT_TRACK_ID,
get_current_time,
get_tag_value,
encrypt_pwd,
encrypt_payment_pwd,
save_image,
open_image,
response_status,
parse_json,
parse_sku_id,
parse_area_id,
parse_items_dict,
list_to_str
)


class Assistant(object):
Expand All @@ -28,15 +48,15 @@ def __init__(self):
self.risk_control = ''
self.eid = global_config.get('config', 'eid') or DEFAULT_EID
self.fp = global_config.get('config', 'fp') or DEFAULT_FP
self.track_id = '9643cbd55bbbe103eef18a213e069eb0'
self.track_id = DEFAULT_TRACK_ID

self.seckill_init_info = dict()
self.seckill_order_data = dict()
self.seckill_url = dict()

try:
self._load_cookies()
except Exception as e:
except Exception:
pass

def _load_cookies(self):
Expand Down Expand Up @@ -87,8 +107,8 @@ def _need_auth_code(self, username):
print('获取是否需要验证码失败')
return False

js = json.loads(resp.text[1:-1]) # ({"verifycode":true})
return js['verifycode']
resp_json = json.loads(resp.text[1:-1]) # ({"verifycode":true})
return resp_json['verifycode']

def _get_auth_code(self, uuid):
image_file = os.path.join(os.getcwd(), 'jd_authcode.jpg')
Expand Down Expand Up @@ -124,27 +144,27 @@ def _get_login_data(self):
soup = BeautifulSoup(page.text, "html.parser")
input_list = soup.select('.form input')

data = dict()
data['sa_token'] = input_list[0]['value']
data['uuid'] = input_list[1]['value']
data['_t'] = input_list[4]['value']
data['loginType'] = input_list[5]['value']
data['pubKey'] = input_list[7]['value']
# eid & fp are generated by local javascript code according to browser environment
data['eid'] = self.eid
data['fp'] = self.fp
return data
return {
'sa_token': input_list[0]['value'],
'uuid': input_list[1]['value'],
'_t': input_list[4]['value'],
'loginType': input_list[5]['value'],
'pubKey': input_list[7]['value'],
'eid': self.eid,
'fp': self.fp,
}

def login_by_username(self):
if self.is_login:
print(get_current_time(), '登录成功')
return
return True

username = input('账号:')
password = input('密码:')
if (not username) or (not password):
print(get_current_time(), '用户名或密码不能为空')
return
return False
self.username = username

data = self._get_login_data()
Expand Down Expand Up @@ -187,27 +207,27 @@ def login_by_username(self):
return True

def _get_login_result(self, resp):
js = parse_json(resp.text)
resp_json = parse_json(resp.text)
error_msg = ''
if 'success' in js:
if 'success' in resp_json:
# {"success":"http://www.jd.com"}
return True
elif 'emptyAuthcode' in js:
elif 'emptyAuthcode' in resp_json:
# {'_t': '_t', 'emptyAuthcode': '请输入验证码'}
# {'_t': '_t', 'emptyAuthcode': '验证码不正确或验证码已过期'}
error_msg = js['emptyAuthcode']
elif 'username' in js:
error_msg = resp_json['emptyAuthcode']
elif 'username' in resp_json:
# {'_t': '_t', 'username': '账户名不存在,请重新输入'}
# {'username': '服务器繁忙,请稍后再试', 'venture': 'xxxx', 'p': 'xxxx', 'ventureRet': 'http://www.jd.com/', '_t': '_t'}
if js['username'] == '服务器繁忙,请稍后再试':
error_msg = js['username'] + '(预计账户存在风险,需短信激活)'
if resp_json['username'] == '服务器繁忙,请稍后再试':
error_msg = resp_json['username'] + '(预计账户存在风险,需短信激活)'
else:
error_msg = js['username']
elif 'pwd' in js:
error_msg = resp_json['username']
elif 'pwd' in resp_json:
# {'pwd': '账户名与密码不匹配,请重新输入', '_t': '_t'}
error_msg = js['pwd']
error_msg = resp_json['pwd']
else:
error_msg = js
error_msg = resp_json
print(get_current_time(), error_msg)
return False

Expand Down Expand Up @@ -252,13 +272,13 @@ def _get_QRcode_ticket(self):
print(get_current_time(), '获取二维码扫描结果出错')
return False

js = parse_json(resp.text)
if js['code'] != 200:
print(get_current_time(), 'Code: {0}, Message: {1}'.format(js['code'], js['msg']))
resp_json = parse_json(resp.text)
if resp_json['code'] != 200:
print(get_current_time(), 'Code: {0}, Message: {1}'.format(resp_json['code'], resp_json['msg']))
return None
else:
print(get_current_time(), '已完成手机客户端确认')
return js['ticket']
return resp_json['ticket']

def _validate_QRcode_ticket(self, ticket):
url = 'https://passport.jd.com/uc/qrCodeTicketValidation'
Expand All @@ -271,11 +291,11 @@ def _validate_QRcode_ticket(self, ticket):
if not response_status(resp):
return False

js = json.loads(resp.text)
if js['returnCode'] == 0:
resp_json = json.loads(resp.text)
if resp_json['returnCode'] == 0:
return True
else:
print(get_current_time(), js)
print(get_current_time(), resp_json)
return False

def login_by_QRcode(self):
Expand Down Expand Up @@ -327,9 +347,9 @@ def _get_reserve_url(self, sku_id):
'Referer': 'https://item.jd.com/{}.html'.format(sku_id),
}
resp = self.sess.get(url=url, params=payload, headers=headers)
js = parse_json(resp.text)
resp_json = parse_json(resp.text)
# {"type":"1","hasAddress":false,"riskCheck":"0","flag":false,"num":941723,"stime":"2018-10-12 12:40:00","plusEtime":"","qiangEtime":"","showPromoPrice":"0","qiangStime":"","state":2,"sku":100000287121,"info":"\u9884\u7ea6\u8fdb\u884c\u4e2d","isJ":0,"address":"","d":48824,"hidePrice":"0","yueEtime":"2018-10-19 15:01:00","plusStime":"","isBefore":0,"url":"//yushou.jd.com/toYuyue.action?sku=100000287121&key=237af0174f1cffffd227a2f98481a338","etime":"2018-10-19 15:01:00","plusD":48824,"category":"4","plusType":0,"yueStime":"2018-10-12 12:40:00"};
reserve_url = js.get('url')
reserve_url = resp_json.get('url')
return 'https:' + reserve_url if reserve_url else None

def make_reserve(self, sku_id):
Expand Down Expand Up @@ -366,11 +386,11 @@ def get_user_info(self):
}
try:
resp = self.sess.get(url=url, params=payload, headers=headers)
js = parse_json(resp.text)
resp_json = parse_json(resp.text)
# many user info are included in response, now return nick name in it
# jQuery2381773({"imgUrl":"//storage.360buyimg.com/i.imageUpload/xxx.jpg","lastLoginTime":"","nickName":"xxx","plusStatus":"0","realName":"xxx","userLevel":x,"userScoreVO":{"accountScore":xx,"activityScore":xx,"consumptionScore":xxxxx,"default":false,"financeScore":xxx,"pin":"xxx","riskScore":x,"totalScore":xxxxx}})
return js.get('nickName') or 'jd'
except Exception as e:
return resp_json.get('nickName') or 'jd'
except Exception:
return 'jd'

def _get_item_detail_page(self, sku_id):
Expand All @@ -391,8 +411,8 @@ def get_single_item_stock(self, sku_id, area):
cat = self.item_cat.get(sku_id)
if not cat:
page = self._get_item_detail_page(sku_id)
m = re.search(r'cat: \[(.*?)\]', page.text)
cat = m.group(1)
match = re.search(r'cat: \[(.*?)\]', page.text)
cat = match.group(1)
self.item_cat[sku_id] = cat

url = 'https://c0.3.cn/stock'
Expand All @@ -413,9 +433,9 @@ def get_single_item_stock(self, sku_id, area):
}
resp = requests.get(url=url, params=payload, headers=headers)

js = parse_json(resp.text)
stock_state = js['StockState'] # 33 -- 现货 34 -- 无货 40 -- 可配货
stock_state_name = js['StockStateName']
resp_json = parse_json(resp.text)
stock_state = resp_json['StockState'] # 33 -- 现货 34 -- 无货 40 -- 可配货
stock_state_name = resp_json['StockStateName']
return stock_state, stock_state_name # (33, '现货') (34, '无货') (36, '采购中') (40, '可配货')

def get_multi_item_stock(self, sku_ids, area):
Expand Down Expand Up @@ -458,8 +478,8 @@ def get_multi_item_stock(self, sku_ids, area):
data = json.dumps(data)

resp = self.sess.post(url=url, headers=headers, data=data)
js = parse_json(resp.text)
result = js.get('result')
resp_json = parse_json(resp.text)
result = resp_json.get('result')

stock = True
for sku_id in result:
Expand All @@ -482,7 +502,7 @@ def if_item_in_stock(self, sku_ids, area):

# 单个商品查询库存
stock_code = self.get_single_item_stock(sku_ids[0], area)[0] # 库存状态码
return True if stock_code == 33 or stock_code == 40 else False # 现货(33)和可配货(40)均可以下单
return stock_code in (33, 40) # 现货(33)和可配货(40)均可以下单

def get_item_price(self, sku_id):
"""获取商品价格
Expand All @@ -496,8 +516,7 @@ def get_item_price(self, sku_id):
'skuIds': 'J_' + sku_id,
}
resp = self.sess.get(url=url, params=payload)
js = parse_json(resp.text)
return js['p']
return parse_json(resp.text).get('p')

def add_item_to_cart(self, sku_ids):
"""添加商品到购物车
Expand Down Expand Up @@ -533,7 +552,7 @@ def add_item_to_cart(self, sku_ids):
tag = soup.select('h3.ftx-02') # [<h3 class="ftx-02">商品已成功加入购物车!</h3>]
if not tag:
print(get_current_time(), '{0}添加到购物车失败'.format(sku_id))
return False
return
print(get_current_time(), '{0} x {1} 已成功加入购物车'.format(sku_id, count))

def clear_cart(self):
Expand Down Expand Up @@ -727,7 +746,7 @@ def submit_order(self):

try:
resp = self.sess.post(url=url, data=data, headers=headers)
js = json.loads(resp.text)
resp_json = json.loads(resp.text)

# 返回信息示例:
# 下单失败
Expand All @@ -739,11 +758,11 @@ def submit_order(self):
# 下单成功
# {'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 js.get('success'):
print(get_current_time(), '订单提交成功! 订单号:{0}'.format(js.get('orderId')))
if resp_json.get('success'):
print(get_current_time(), '订单提交成功! 订单号:{0}'.format(resp_json.get('orderId')))
return True
else:
message, result_code = js.get('message'), js.get('resultCode')
message, result_code = resp_json.get('message'), resp_json.get('resultCode')
if result_code == 0:
self._save_invoice()
message = message + '(下单商品可能为第三方商品,将切换为普通发票进行尝试)'
Expand All @@ -752,7 +771,7 @@ def submit_order(self):
elif result_code == 60123:
message = message + '(需要在config.ini文件中配置支付密码)'
print(get_current_time(), '订单提交失败, 错误码:{0}, 返回信息:{1}'.format(result_code, message))
print(get_current_time(), js)
print(get_current_time(), resp_json)
return False
except Exception as e:
print(get_current_time(), e)
Expand Down Expand Up @@ -811,7 +830,7 @@ def get_order_info(self, unpaid=True):
"""
if not self.is_login:
print(get_current_time(), '请先登录再查询订单!')
return False
return

url = 'https://order.jd.com/center/list.action'
payload = {
Expand Down Expand Up @@ -841,7 +860,7 @@ def get_order_info(self, unpaid=True):

# check if order is waiting for payment
# wait_payment = bool(table_body.select('a.btn-pay'))
wait_payment = True if "等待付款" in order_status else False
wait_payment = "等待付款" in order_status

# only show unpaid orders if unpaid=True
if unpaid and (not wait_payment):
Expand Down Expand Up @@ -908,10 +927,10 @@ def _get_seckill_url(self, sku_id):
}
while True:
resp = self.sess.get(url=url, headers=headers, params=payload)
js = parse_json(resp.text)
if js.get('url'):
resp_json = parse_json(resp.text)
if resp_json.get('url'):
# https://divide.jd.com/user_routing?skuId=8654289&sn=c3f4ececd8461f0e4d7267e96a91e0e0&from=pc
router_url = 'https:' + js.get('url')
router_url = 'https:' + resp_json.get('url')
# https://marathon.jd.com/captcha.html?skuId=8654289&sn=c3f4ececd8461f0e4d7267e96a91e0e0&from=pc
seckill_url = router_url.replace('divide', 'marathon').replace('user_routing', 'captcha.html')
print(get_current_time(), "抢购链接获取成功: {0}".format(seckill_url))
Expand Down Expand Up @@ -1039,11 +1058,11 @@ def submit_seckill_order(self, sku_id, num=1):
headers = {
'User-Agent': USER_AGENT,
'Host': 'marathon.jd.com',
'Referer': 'https://marathon.jd.com/seckill/seckill.action?skuId={0}&num={1}&rid={2}'
.format(sku_id, num, int(time.time())),
'Referer': 'https://marathon.jd.com/seckill/seckill.action?skuId={0}&num={1}&rid={2}'.format(
sku_id, num, int(time.time())),
}
resp = self.sess.post(url=url, params=payload, data=self.seckill_order_data.get(sku_id), headers=headers)
js = parse_json(resp.text)
resp_json = parse_json(resp.text)
# 返回信息
# 抢购失败:
# {'errorMessage': '很遗憾没有抢到,再接再厉哦。', 'orderId': 0, 'resultCode': 60074, 'skuId': 0, 'success': False}
Expand All @@ -1052,15 +1071,15 @@ def submit_seckill_order(self, sku_id, num=1):
# 抢购成功:
# {"appUrl":"xxxxx","orderId":820227xxxxx,"pcUrl":"xxxxx","resultCode":0,"skuId":0,"success":true,"totalMoney":"xxxxx"}

if js.get('success'):
order_id = js.get('orderId')
total_money = js.get('totalMoney')
pay_url = 'https:' + js.get('pcUrl')
if resp_json.get('success'):
order_id = resp_json.get('orderId')
total_money = resp_json.get('totalMoney')
pay_url = 'https:' + resp_json.get('pcUrl')
print(get_current_time(), '抢购成功,订单号: {0}, 总价: {1}, 电脑端付款链接: {2}'
.format(order_id, total_money, pay_url))
return True
else:
print(get_current_time(), '抢购失败,返回信息: {}'.format(js))
print(get_current_time(), '抢购失败,返回信息: {}'.format(resp_json))
return False

def exec_seckill(self, sku_id, retry=4, interval=4, num=1):
Expand Down
2 changes: 2 additions & 0 deletions util.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

DEFAULT_FP = '18c7d83a053e6bbb51f755aea595bbb8'

DEFAULT_TRACK_ID = '9643cbd55bbbe103eef18a213e069eb0'


def encrypt_pwd(password, public_key=RSA_PUBLIC_KEY):
rsa_key = RSA.importKey(public_key)
Expand Down

0 comments on commit f4e8986

Please sign in to comment.