Skip to content

Commit

Permalink
添加二维码登录方式
Browse files Browse the repository at this point in the history
  • Loading branch information
huanghyw committed Dec 21, 2020
1 parent 121cd42 commit fbccc95
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 60 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ venv.bak/
logs/
.test
test/
cookies/
*.cookies
QRcode.png

# jupyter
.ipynb_checkpoints
Expand Down
10 changes: 10 additions & 0 deletions exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python
# -*- encoding=utf8 -*-
from jd_logger import logger


class SKException(Exception):

def __init__(self, message):
super().__init__(message)
logger.error(message)
218 changes: 184 additions & 34 deletions jd_spider_requests.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import random
import sys
import time
import requests
import functools
import json
import os
import pickle

from lxml import etree
from jd_logger import logger
from timer import Timer
import requests
from util import parse_json, get_session, get_sku_title,send_wechat
from util import parse_json, send_wechat, get_session, response_status, save_image, open_image
from config import global_config
from concurrent.futures import ProcessPoolExecutor
from exception import SKException


class JdSeckill(object):
Expand All @@ -20,6 +27,11 @@ def __init__(self):
self.seckill_order_data = dict()
self.timers = Timer()
self.default_user_agent = global_config.getRaw('config', 'DEFAULT_USER_AGENT')
self.headers = {'User-Agent': self.default_user_agent}
self.is_login = False
self.nick_name = None

self._load_cookies()

def reserve(self):
"""
Expand All @@ -33,9 +45,6 @@ def seckill(self):
"""
self.__seckill()

def system_exit(self):
sys.exit(1)

def wait_some_time(self):
time.sleep(random.randint(100, 300) / 1000)

Expand All @@ -52,7 +61,7 @@ def __reserve(self):
"""
预约
"""
self.login()
self._validate_cookies()
while True:
try:
self.make_reserve()
Expand All @@ -64,45 +73,178 @@ def __seckill(self):
"""
抢购
"""
self.login()
self._validate_cookies()
while True:
try:
self.request_seckill_url()
while True:
self.request_seckill_checkout_page()
if self.submit_seckill_order():
self.system_exit()
self.submit_seckill_order()
except Exception as e:
logger.info('抢购发生异常,稍后继续执行!', e)
self.wait_some_time()

def login(self):
for flag in range(1, 3):
try:
targetURL = 'https://order.jd.com/center/list.action'
payload = {
'rid': str(int(time.time() * 1000)),
}
resp = self.session.get(
url=targetURL, params=payload, allow_redirects=False)
if resp.status_code == requests.codes.OK:
logger.info('校验是否登录[成功]')
logger.info('用户:{}'.format(self.get_username()))
return True
else:
logger.info('校验是否登录[失败]')
logger.info('请重新输入cookie')
time.sleep(1)
continue
except Exception as e:
logger.info('第【%s】次失败请重新获取cookie', flag)
time.sleep(1)
continue
self.system_exit()
def _load_cookies(self):
cookies_file = ''
for name in os.listdir('./cookies'):
if name.endswith('.cookies'):
cookies_file = './cookies/{0}'.format(name)
break
if cookies_file == '':
return
with open(cookies_file, 'rb') as f:
local_cookies = pickle.load(f)
self.session.cookies.update(local_cookies)
self.is_login = self._validate_cookies()

def _save_cookies(self):
cookies_file = './cookies/{0}.cookies'.format(self.nick_name)
directory = os.path.dirname(cookies_file)
if not os.path.exists(directory):
os.makedirs(directory)
with open(cookies_file, 'wb') as f:
pickle.dump(self.session.cookies, f)

def _validate_cookies(self):
"""验证cookies是否有效(是否登陆)
通过访问用户订单列表页进行判断:若未登录,将会重定向到登陆页面。
:return: cookies是否有效 True/False
"""
url = 'https://order.jd.com/center/list.action'
payload = {
'rid': str(int(time.time() * 1000)),
}
try:
resp = self.session.get(url=url, params=payload, allow_redirects=False)
if resp.status_code == requests.codes.OK:
return True
except Exception as e:
logger.error(e)

self.session = get_session()
return False

def _get_login_page(self):
url = "https://passport.jd.com/new/login.aspx"
page = self.session.get(url, headers=self.headers)
return page

def _get_QRcode(self):
url = 'https://qr.m.jd.com/show'
payload = {
'appid': 133,
'size': 147,
't': str(int(time.time() * 1000)),
}
headers = {
'User-Agent': self.default_user_agent,
'Referer': 'https://passport.jd.com/new/login.aspx',
}
resp = self.session.get(url=url, headers=headers, params=payload)

if not response_status(resp):
logger.info('获取二维码失败')
return False

QRCode_file = 'QRcode.png'
save_image(resp, QRCode_file)
logger.info('二维码获取成功,请打开京东APP扫描')
open_image(QRCode_file)
return True

def check_login(func):
"""用户登陆态校验装饰器。若用户未登陆,则调用扫码登陆"""

@functools.wraps(func)
def new_func(self, *args, **kwargs):
if not self.is_login:
logger.info("{0} 需登陆后调用,开始扫码登陆".format(func.__name__))
self.login_by_QRcode()
return func(self, *args, **kwargs)

return new_func

def _get_QRcode_ticket(self):
url = 'https://qr.m.jd.com/check'
payload = {
'appid': '133',
'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)),
'token': self.session.cookies.get('wlfstk_smdl'),
'_': str(int(time.time() * 1000)),
}
headers = {
'User-Agent': self.default_user_agent,
'Referer': 'https://passport.jd.com/new/login.aspx',
}
resp = self.session.get(url=url, headers=headers, params=payload)

if not response_status(resp):
logger.error('获取二维码扫描结果异常')
return False

resp_json = parse_json(resp.text)
if resp_json['code'] != 200:
logger.info('Code: %s, Message: %s', resp_json['code'], resp_json['msg'])
return None
else:
logger.info('已完成手机客户端确认')
return resp_json['ticket']

def _validate_QRcode_ticket(self, ticket):
url = 'https://passport.jd.com/uc/qrCodeTicketValidation'
headers = {
'User-Agent': self.default_user_agent,
'Referer': 'https://passport.jd.com/uc/login?ltype=logout',
}
resp = self.session.get(url=url, headers=headers, params={'t': ticket})

if not response_status(resp):
return False

resp_json = json.loads(resp.text)
if resp_json['returnCode'] == 0:
return True
else:
logger.info(resp_json)
return False

def login_by_QRcode(self):
"""二维码登陆
:return:
"""
if self.is_login:
logger.info('登录成功')
return

self._get_login_page()

# download QR code
if not self._get_QRcode():
raise SKException('二维码下载失败')

# get QR code ticket
ticket = None
retry_times = 85
for _ in range(retry_times):
ticket = self._get_QRcode_ticket()
if ticket:
break
time.sleep(2)
else:
raise SKException('二维码过期,请重新获取扫描')

# validate QR code ticket
if not self._validate_QRcode_ticket(ticket):
raise SKException('二维码信息校验失败')

logger.info('二维码登录成功')
self.is_login = True
self.nick_name = self.get_username()
self._save_cookies()

def make_reserve(self):
"""商品预约"""
logger.info('商品名称:{}'.format(get_sku_title()))
logger.info('商品名称:{}'.format(self.get_sku_title()))
url = 'https://yushou.jd.com/youshouinfo.action?'
payload = {
'callback': 'fetchJSON',
Expand Down Expand Up @@ -154,6 +296,14 @@ def get_username(self):
# 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 parse_json(resp.text).get('nickName')

def get_sku_title(self):
"""获取商品名称"""
url = 'https://item.jd.com/{}.html'.format(global_config.getRaw('config', 'sku_id'))
resp = self.session.get(url).content
x_data = etree.HTML(resp)
sku_title = x_data.xpath('/html/head/title/text()')
return sku_title[0]

def get_seckill_url(self):
"""获取商品的抢购链接
点击"抢购"按钮后,会有两次302跳转,最后到达订单结算页面
Expand Down Expand Up @@ -191,7 +341,7 @@ def get_seckill_url(self):
def request_seckill_url(self):
"""访问商品的抢购链接(用于设置cookie等"""
logger.info('用户:{}'.format(self.get_username()))
logger.info('商品名称:{}'.format(get_sku_title()))
logger.info('商品名称:{}'.format(self.get_sku_title()))
self.timers.start()
self.seckill_url[self.sku_id] = self.get_seckill_url()
logger.info('访问商品的抢购连接...')
Expand Down
1 change: 1 addition & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
print(a)

jd_seckill = JdSeckill()
jd_seckill.login_by_QRcode()
choice_function = input('请选择:')
if choice_function == '1':
jd_seckill.reserve()
Expand Down
Loading

0 comments on commit fbccc95

Please sign in to comment.