import math import functools from lxml import html import requests import time import json import random from selenium import webdriver from selenium.webdriver.chrome.options import Options from concurrent.futures import ProcessPoolExecutor from exception import SKException from bs4 import BeautifulSoup from config import global_config from logger import logger from login import SpiderSession, QrLogin from message import sendMessage class Waiter(): def __init__(self): self.spider_session = SpiderSession() self.spider_session.load_cookies_from_local() self.session = self.spider_session.get_session() self.qrlogin = QrLogin(self.spider_session) self.skuids = global_config.getRaw("config", "sku_id") self.area = global_config.getRaw("config", "area") self.eid = global_config.getRaw("config", "eid") self.fp = global_config.getRaw("config", "fp") self.user_agent = self.spider_session.user_agent def login_by_qrcode(self): """ 二维码登陆 :return: """ if self.qrlogin.is_login: logger.info('登录成功') return self.qrlogin.login_by_qrcode() if self.qrlogin.is_login: self.nick_name = self.getUsername() self.spider_session.save_cookies_to_local(self.nick_name) else: raise SKException("二维码登录失败!") def check_login(func): """ 用户登陆态校验装饰器。若用户未登陆,则调用扫码登陆 """ @functools.wraps(func) def new_func(self, *args, **kwargs): if not self.qrlogin.is_login: logger.info("{0} 需登陆后调用,开始扫码登陆".format(func.__name__)) self.login_by_qrcode() return func(self, *args, **kwargs) return new_func def waitForSell(self): self._waitForSell() def get_tag_value(self, tag, key='', index=0): if key: value = tag[index].get(key) else: value = tag[index].text return value.strip(' \t\r\n') def response_status(self, resp): if resp.status_code != requests.codes.OK: print('Status: %u, Url: %s' % (resp.status_code, resp.url)) return False return True def getUsername(self): userName_Url = 'https://passport.jd.com/new/helloService.ashx?callback=jQuery339448&_=' + str( int(time.time() * 1000)) self.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", "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://order.jd.com/center/list.action", "Connection": "keep-alive" } resp = self.session.get(url=userName_Url, allow_redirects=True) resultText = resp.text resultText = resultText.replace('jQuery339448(', '') resultText = resultText.replace(')', '') usernameJson = json.loads(resultText) logger.info('登录账号名称' + usernameJson['nick']) return usernameJson['nick'] 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 = html.etree.HTML(resp) sku_title = x_data.xpath('/html/head/title/text()') return sku_title[0] @check_login def waitAndBuy_by_proc_pool(self): """ 多进程进行抢购 work_count:进程数量 """ work_count = eval(global_config.getRaw('settings', 'work_count')) with ProcessPoolExecutor(work_count) as pool: for i in range(work_count): pool.submit(self.buy) ''' 检查是否有货 ''' def check_item_stock(self): stockurl = 'http://c0.3.cn/stock?skuId=' + self.skuids + '&cat=652,829,854&area=' + self.area + '&extraParam={%22originid%22:%221%22}' response = self.session.get(stockurl) resp = self.session.get(stockurl) jsparser = json.loads(resp.text) # 33 有货 34 无货 if jsparser['StockState'] == 33 and jsparser['StockStateName'] == '现货': print('库存状态:', jsparser['StockStateName']) return True else: print('库存状态:{}(无现货)'.format(jsparser['StockStateName'])) return False def _waitForSell(self): self.getInfo_selenium() def getInfo_selenium(self): """ 等待进行抢购 """ config_headless = True chrome_options = Options() chrome_options.add_argument(self.user_agent) chrome_options.add_argument("--profile-directory=Default") chrome_options.add_argument("--whitelisted-ips") chrome_options.add_argument("--start-maximized") chrome_options.add_argument("--disable-extensions") chrome_options.add_argument("--disable-plugins-discovery") if config_headless == True: # 此处开启后为无头浏览器 chrome_options.add_argument('--headless') chrome_options.add_argument('--no-sandbox') chrome_options.add_argument('--disable-dev-shm-usage') chrome_options.add_argument('blink-settings=imagesEnabled=false') chrome_options.add_argument('--disable-gpu') logger.info("正在等待商品上架:{}".format(self.get_sku_title()[:40] + " ......")) driver = webdriver.Chrome('/usr/local/bin/chromedriver', options=chrome_options) driver.maximize_window() while True: state = False url = 'https://item.jd.com/{}.html'.format(global_config.getRaw('config', 'sku_id')) driver.get(url) time.sleep(5) buttonValue = driver.find_element_by_xpath( "/html/body/div[@class='w']/div[@class='product-intro clearfix']/div[@class='itemInfo-wrap']/div[@class='summary p-choose-wrap']/div[@id='choose-btns']/a[@id='InitCartUrl']").get_attribute( "class") # joinCart = x_data.xpath("/html/body/div[@class='w']/div[@class='product-intro clearfix']/div[@class='itemInfo-wrap']/div[@class='summary p-choose-wrap']/div[@id='choose-btns']/a[@id='InitCartUrl']/@class") # notice = x_data.xpath("/html/body/div[@class='w']/div[@class='product-intro clearfix']/div[@class='itemInfo-wrap']/div[@class='summary p-choose-wrap']/div[@id='choose-btns']/a[@id='btn-notify']/text()") if buttonValue == "btn-special1 btn-lg btn-disable": # 没货 State = False else: state = True if state: driver.quit() sendMessage("商品上架: {}".format(self.get_sku_title()[:40] + " ......")) logger.info("商品上架: {}".format(self.get_sku_title()[:40] + " ......")) # self.seckill_by_proc_pool() self.waitAndBuy_by_proc_pool() else: pass ''' 取消勾选购物车中的所有商品 ''' def cancel_select_all_cart_item(self): url = "https://cart.jd.com/cancelAllItem.action" data = { 't': 0, 'outSkus': '', 'random': random.random() } resp = self.session.post(url, data=data) if resp.status_code != requests.codes.OK: print('Status: %u, Url: %s' % (resp.status_code, resp.url)) return False return True ''' 勾选购物车中的所有商品 ''' def select_all_cart_item(self): url = "https://cart.jd.com/selectAllItem.action" data = { 't': 0, 'outSkus': '', 'random': random.random() } resp = self.session.post(url, data=data) if resp.status_code != requests.codes.OK: print('Status: %u, Url: %s' % (resp.status_code, resp.url)) return False return True ''' 删除购物车选中商品 ''' def remove_item(self): url = "https://cart.jd.com/batchRemoveSkusFromCart.action" data = { 't': 0, 'null': '', 'outSkus': '', 'random': random.random(), 'locationId': '19-1607-4773-0' } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.37", "Accept": "application/json, text/javascript, */*; q=0.01", "Referer": "https://cart.jd.com/cart.action", "Host": "cart.jd.com", "Content-Type": "application/x-www-form-urlencoded", "Accept-Encoding": "gzip, deflate, br", "Accept-Encoding": "zh-CN,zh;q=0.9,ja;q=0.8", "Origin": "https://cart.jd.com", "Connection": "keep-alive" } resp = self.session.post(url, data=data, headers=headers) logger.info('清空购物车') if resp.status_code != requests.codes.OK: print('Status: %u, Url: %s' % (resp.status_code, resp.url)) return False return True ''' 购物车详情 ''' def cart_detail(self): url = 'https://cart.jd.com/cart.action' 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://order.jd.com/center/list.action", "Host": "cart.jd.com", "Connection": "keep-alive" } resp = self.session.get(url, headers=headers) soup = BeautifulSoup(resp.text, "html.parser") cart_detail = dict() for item in soup.find_all(class_='item-item'): try: sku_id = item['skuid'] # 商品id except Exception as e: logger.info('购物车中有套装商品,跳过') continue try: # 例如:['increment', '8888', '100001071956', '1', '13', '0', '50067652554'] # ['increment', '8888', '100002404322', '2', '1', '0'] item_attr_list = item.find(class_='increment')['id'].split('_') p_type = item_attr_list[4] promo_id = target_id = item_attr_list[-1] if len(item_attr_list) == 7 else 0 cart_detail[sku_id] = { 'name': self.get_tag_value(item.select('div.p-name a')), # 商品名称 'verder_id': item['venderid'], # 商家id 'count': int(item['num']), # 数量 'unit_price': self.get_tag_value(item.select('div.p-price strong'))[1:], # 单价 'total_price': self.get_tag_value(item.select('div.p-sum strong'))[1:], # 总价 'is_selected': 'item-selected' in item['class'], # 商品是否被勾选 'p_type': p_type, 'target_id': target_id, 'promo_id': promo_id } except Exception as e: logger.error("商品%s在购物车中的信息无法解析,报错信息: %s,该商品自动忽略", sku_id, e) logger.info('购物车信息:%s', cart_detail) return cart_detail ''' 修改购物车商品的数量 ''' def change_item_num_in_cart(self, sku_id, vender_id, num, p_type, target_id, promo_id): url = "https://cart.jd.com/changeNum.action" data = { 't': 0, 'venderId': vender_id, 'pid': sku_id, 'pcount': num, 'ptype': p_type, 'targetId': target_id, 'promoID': promo_id, 'outSkus': '', 'random': random.random(), # 'locationId' } self.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", "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://cart.jd.com/cart", "Connection": "keep-alive" } resp = self.session.post(url, data=data) return json.loads(resp.text)['sortedWebCartResult']['achieveSevenState'] == 2 ''' 添加商品到购物车 ''' def add_item_to_cart(self, sku_id): url = 'https://cart.jd.com/gate.action' payload = { 'pid': sku_id, 'pcount': 1, 'ptype': 1, } resp = self.session.get(url=url, params=payload) if 'https://cart.jd.com/cart.action' in resp.url: # 套装商品加入购物车后直接跳转到购物车页面 result = True else: # 普通商品成功加入购物车后会跳转到提示 "商品已成功加入购物车!" 页面 soup = BeautifulSoup(resp.text, "html.parser") result = bool(soup.select('h3.ftx-02')) # [

商品已成功加入购物车!

] if result: logger.info('%s 已成功加入购物车', sku_id) else: logger.error('%s 添加到购物车失败', sku_id) def get_checkout_page_detail(self): """获取订单结算页面信息 该方法会返回订单结算页面的详细信息:商品名称、价格、数量、库存状态等。 :return: 结算信息 dict """ url = 'http://trade.jd.com/shopping/order/getOrderInfo.action' # url = 'https://cart.jd.com/gotoOrder.action' payload = { 'rid': str(int(time.time() * 1000)), } 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://cart.jd.com/cart.action", "Connection": "keep-alive", 'Host': 'trade.jd.com', } try: resp = self.session.get(url=url, params=payload, headers=headers) if not self.response_status(resp): logger.error('获取订单结算页信息失败') return '' soup = BeautifulSoup(resp.text, "html.parser") risk_control = self.get_tag_value(soup.select('input#riskControl'), 'value') order_detail = { 'address': soup.find('span', id='sendAddr').text[5:], # remove '寄送至: ' from the begin 'receiver': soup.find('span', id='sendMobile').text[4:], # remove '收件人:' from the begin 'total_price': soup.find('span', id='sumPayPriceId').text[1:], # remove '¥' from the begin 'items': [] } logger.info("下单信息:%s", order_detail) return order_detail except requests.exceptions.RequestException as e: logger.error('订单结算页面获取异常:%s' % e) except Exception as e: logger.error('下单页面数据解析异常:%s', e) return risk_control def submit_order(self, risk_control): """提交订单 重要: 1.该方法只适用于普通商品的提交订单(即可以加入购物车,然后结算提交订单的商品) 2.提交订单时,会对购物车中勾选✓的商品进行结算(如果勾选了多个商品,将会提交成一个订单) :return: True/False 订单提交结果 """ 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': '[]', '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.eid': eid, # 'submitOrderParam.fp': fp, 'submitOrderParam.needCheck': 1, } def encrypt_payment_pwd(payment_pwd): return ''.join(['u3' + x for x in payment_pwd]) self.payment_pwd="0" if len(self.payment_pwd) > 0: data['submitOrderParam.payPassword'] = encrypt_payment_pwd(self.payment_pwd) 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', } try: resp = self.session.post(url=url, data=data, headers=headers) resp_json = json.loads(resp.text) # 返回信息示例: # 下单失败 # {'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': '请输入支付密码!'} # {'overSea': False, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'orderXml': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60017, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '您多次提交过快,请稍后再试'} # {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60077, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '获取用户订单信息失败'} # {"cartXml":null,"noStockSkuIds":"xxx","reqInfo":null,"hasJxj":false,"addedServiceList":null,"overSea":false,"orderXml":null,"sign":null,"pin":"xxx","needCheckCode":false,"success":false,"resultCode":600157,"orderId":0,"submitSkuNum":0,"deductMoneyFlag":0,"goJumpOrderCenter":false,"payInfo":null,"scaleSkuInfoListVO":null,"purchaseSkuInfoListVO":null,"noSupportHomeServiceSkuList":null,"msgMobile":null,"addressVO":{"pin":"xxx","areaName":"","provinceId":xx,"cityId":xx,"countyId":xx,"townId":xx,"paymentId":0,"selected":false,"addressDetail":"xx","mobile":"xx","idCard":"","phone":null,"email":null,"selfPickMobile":null,"selfPickPhone":null,"provinceName":null,"cityName":null,"countyName":null,"townName":null,"giftSenderConsigneeName":null,"giftSenderConsigneeMobile":null,"gcLat":0.0,"gcLng":0.0,"coord_type":0,"longitude":0.0,"latitude":0.0,"selfPickOptimize":0,"consigneeId":0,"selectedAddressType":0,"siteType":0,"helpMessage":null,"tipInfo":null,"cabinetAvailable":true,"limitKeyword":0,"specialRemark":null,"siteProvinceId":0,"siteCityId":0,"siteCountyId":0,"siteTownId":0,"skuSupported":false,"addressSupported":0,"isCod":0,"consigneeName":null,"pickVOname":null,"shipmentType":0,"retTag":0,"tagSource":0,"userDefinedTag":null,"newProvinceId":0,"newCityId":0,"newCountyId":0,"newTownId":0,"newProvinceName":null,"newCityName":null,"newCountyName":null,"newTownName":null,"checkLevel":0,"optimizePickID":0,"pickType":0,"dataSign":0,"overseas":0,"areaCode":null,"nameCode":null,"appSelfPickAddress":0,"associatePickId":0,"associateAddressId":0,"appId":null,"encryptText":null,"certNum":null,"used":false,"oldAddress":false,"mapping":false,"addressType":0,"fullAddress":"xxxx","postCode":null,"addressDefault":false,"addressName":null,"selfPickAddressShuntFlag":0,"pickId":0,"pickName":null,"pickVOselected":false,"mapUrl":null,"branchId":0,"canSelected":false,"address":null,"name":"xxx","message":null,"id":0},"msgUuid":null,"message":"xxxxxx商品无货"} # {'orderXml': None, 'overSea': False, 'noStockSkuIds': 'xxx', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'cartXml': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 600158, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': {'oldAddress': False, 'mapping': False, 'pin': 'xxx', 'areaName': '', 'provinceId': xx, 'cityId': xx, 'countyId': xx, 'townId': xx, 'paymentId': 0, 'selected': False, 'addressDetail': 'xxxx', 'mobile': 'xxxx', 'idCard': '', 'phone': None, 'email': None, 'selfPickMobile': None, 'selfPickPhone': None, 'provinceName': None, 'cityName': None, 'countyName': None, 'townName': None, 'giftSenderConsigneeName': None, 'giftSenderConsigneeMobile': None, 'gcLat': 0.0, 'gcLng': 0.0, 'coord_type': 0, 'longitude': 0.0, 'latitude': 0.0, 'selfPickOptimize': 0, 'consigneeId': 0, 'selectedAddressType': 0, 'newCityName': None, 'newCountyName': None, 'newTownName': None, 'checkLevel': 0, 'optimizePickID': 0, 'pickType': 0, 'dataSign': 0, 'overseas': 0, 'areaCode': None, 'nameCode': None, 'appSelfPickAddress': 0, 'associatePickId': 0, 'associateAddressId': 0, 'appId': None, 'encryptText': None, 'certNum': None, 'addressType': 0, 'fullAddress': 'xxxx', 'postCode': None, 'addressDefault': False, 'addressName': None, 'selfPickAddressShuntFlag': 0, 'pickId': 0, 'pickName': None, 'pickVOselected': False, 'mapUrl': None, 'branchId': 0, 'canSelected': False, 'siteType': 0, 'helpMessage': None, 'tipInfo': None, 'cabinetAvailable': True, 'limitKeyword': 0, 'specialRemark': None, 'siteProvinceId': 0, 'siteCityId': 0, 'siteCountyId': 0, 'siteTownId': 0, 'skuSupported': False, 'addressSupported': 0, 'isCod': 0, 'consigneeName': None, 'pickVOname': None, 'shipmentType': 0, 'retTag': 0, 'tagSource': 0, 'userDefinedTag': None, 'newProvinceId': 0, 'newCityId': 0, 'newCountyId': 0, 'newTownId': 0, 'newProvinceName': None, 'used': False, 'address': None, 'name': 'xx', 'message': None, 'id': 0}, 'msgUuid': None, 'message': 'xxxxxx商品无货'} # 下单成功 # {'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')) sendMessage('订单提交成功! 订单号:{}'.format(resp_json.get('orderId'))) return True else: message, result_code = resp_json.get('message'), resp_json.get('resultCode') if result_code == 0: # self._save_invoice() message = message + '(下单商品可能为第三方商品,将切换为普通发票进行尝试)' elif result_code == 60077: message = message + '(可能是购物车为空 或 未勾选购物车中商品)' elif result_code == 60123: message = message + '(需要在payment_pwd参数配置支付密码)' logger.info('订单提交失败, 错误码:%s, 返回信息:%s', result_code, message) logger.info(resp_json) return False except Exception as e: logger.error(e) return False ''' 购买环节 测试三次 ''' def buyMask(self, sku_id): retry = eval(global_config.getRaw("config", "retry")) for count in range(retry): logger.info('第[%s/%s]次尝试提交订单', count, 3) self.cancel_select_all_cart_item() cart = self.cart_detail() if sku_id in cart: logger.info('%s 已在购物车中,调整数量为 %s', sku_id, 1) cart_item = cart.get(sku_id) self.change_item_num_in_cart( sku_id=sku_id, vender_id=cart_item.get('vender_id'), num=1, p_type=cart_item.get('p_type'), target_id=cart_item.get('target_id'), promo_id=cart_item.get('promo_id') ) else: self.add_item_to_cart(sku_id) risk_control = self.get_checkout_page_detail() if risk_control == '刷新太频繁了': return False if len(risk_control) > 0: if self.submit_order(risk_control): return True logger.info('休息%ss', 3) time.sleep(3) else: logger.info('执行结束,提交订单失败!') return False ''' 查询库存 ''' ''' update by rlacat 解决skuid长度过长(超过99个)导致无法查询问题 ''' def check_stock(self): st_tmp = [] len_arg = 70 #print("skustr:",skuidStr) #print("skuids:",len(skuids)) skuid_nums = len(self.skuids) skuid_batchs = math.ceil(skuid_nums / len_arg) #print("skuid_batchs:",skuid_batchs) if(skuid_batchs > 1): for i in range(0,skuid_batchs): if(len_arg*(i+1) <= len(self.skuids)): #print("取个数:",len_arg*i,"至",len_arg*(i+1)) skuidStr = ','.join(self.skuids[len_arg*i:len_arg*(i+1)]) st_tmp+=self.check_stock_tmp(skuidStr,self.skuids[len_arg*i:len_arg*(i+1)]) else: #print("取个数:",len_arg*i,"至",len_arg*(i+1)) skuidStr = ','.join(self.skuids[len_arg*i:skuid_nums])#skuid配置的最后一段 #print(skuidStr) st_tmp+=self.check_stock_tmp(skuidStr,self.skuids[len_arg*i:skuid_nums]) else: #<=1的情况 skuidStr = ','.join(self.skuids) st_tmp=self.check_stock_tmp(skuidStr,self.skuids) return st_tmp def check_stock_tmp(self, skuidString,skuids_a): callback = 'jQuery' + str(random.randint(1000000, 9999999)) 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://cart.jd.com/cart.action", "Connection": "keep-alive", } url = 'https://c0.3.cn/stocks' payload = { 'type': 'getstocks', 'skuIds': skuidString, 'area': self.area, 'callback': callback, '_': int(time.time() * 1000), } resp = self.session.get(url=url, params=payload, headers=headers) resptext = resp.text.replace(callback + '(', '').replace(')', '') respjson = json.loads(resptext) inStockSkuid = [] nohasSkuid = [] #print(resptext,respjson) for i in skuids_a: #print("当前处理:",i) if(respjson[i]['StockStateName'] != '无货'): inStockSkuid.append(i) else: nohasSkuid.append(i) #print(nohasSkuid) logger.info('[%s]无货', ','.join(nohasSkuid)) return inStockSkuid @check_login def buy(self): sku_id = global_config.getRaw('config', 'sku_id') retry = eval(global_config.getRaw("config", "retry")) for count in range(retry): logger.info('第[%s/%s]次尝试提交订单', count, retry) self.cancel_select_all_cart_item() cart = self.cart_detail() if sku_id in cart: logger.info('%s 已在购物车中,调整数量为 %s', sku_id, 1) cart_item = cart.get(sku_id) self.change_item_num_in_cart( sku_id=sku_id, vender_id=cart_item.get('vender_id'), num=1, p_type=cart_item.get('p_type'), target_id=cart_item.get('target_id'), promo_id=cart_item.get('promo_id') ) else: self.add_item_to_cart(sku_id) risk_control = self.get_checkout_page_detail() if risk_control == '刷新太频繁了': return False if len(risk_control) > 0: if self.submit_order(risk_control): return True logger.info('休息%ss', 3) time.sleep(3) else: logger.info('执行结束,提交订单失败!') return False