def __init__(self, mail_user, mail_pwd, mail_host=''): if global_config.getRaw('messenger', 'email_enable') == 'false': return smtpObj = smtplib.SMTP() # 没传会自动判断 判断不出来默认QQ邮箱 if mail_host: self.mail_host = mail_host elif mail_user.endswith('163.com'): self.mail_host = 'smtp.163.com' elif mail_user.endswith(('sina.com', 'sina.cn')): self.mail_host = 'smtp.163.com' elif mail_user.endswith('qq.com'): self.mail_host = 'smtp.qq.com' elif mail_user.endswith('sohu.com'): self.mail_host = 'smtp.sohu.com' else: self.mail_host = 'smtp.qq.com' self.mail_user = mail_user self.is_login = False try: smtpObj.connect(mail_host, 25) smtpObj.login(mail_user, mail_pwd) self.is_login = True except Exception as e: logger.info('邮箱登录失败!', e) self.smtpObj = smtpObj
def send(self, title, msg, receivers: list, img=''): """ 发送smtp邮件至收件人 :param title: :param msg: 如果发送图片,需在msg内嵌入<img src='cid:xxx'>,xxx为图片名 :param receivers: :param img: 图片名 :return: """ if self.is_login: message = MIMEMultipart('alternative') msg_html = MIMEText(msg, 'html', 'utf-8') message.attach(msg_html) message['Subject'] = title message['From'] = self.mail_user if img: with open(img, "rb") as f: msg_img = MIMEImage(f.read()) msg_img.add_header('Content-ID', img) message.attach(msg_img) try: self.smtpObj.sendmail(self.mail_user, receivers, message.as_string()) except Exception as e: logger.info('邮件发送失败!', e) else: logger.info('邮箱未登录')
def _get_seckill_init_info(self): """获取秒杀初始化信息(包括:地址,发票,token) :return: 初始化信息组成的dict """ logger.info('获取秒杀初始化信息...') url = 'https://marathon.jd.com/seckillnew/orderService/pc/init.action' data = { 'sku': self.sku_id, 'num': self.seckill_num, 'isModifyAddress': 'false', } headers = { 'User-Agent': self.user_agent, 'Host': 'marathon.jd.com', 'referer': 'https://marathon.jd.com/seckill/seckill.action?skuId={0}&num={1}&rid={2}'.format( self.sku_id, self.seckill_num, int(time.time())), } resp = self.session.post(url=url, data=data, headers=headers) resp_json = None try: resp_json = parse_json(resp.text) except Exception: raise SKException('获取秒杀初始化信息失败,返回信息:{}'.format(resp.text[0: 128])) return resp_json
def make_reserve(self): """商品预约""" logger.info('商品名称:{}'.format(self.get_sku_title())) url = 'https://yushou.jd.com/youshouinfo.action?' payload = { 'callback': 'fetchJSON', 'sku': self.sku_id, '_': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id), } resp = self.session.get(url=url, params=payload, headers=headers) resp_json = parse_json(resp.text) reserve_url = resp_json.get('url') self.timers.start() while True: try: self.session.get(url='https:' + reserve_url) logger.info('预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约') if global_config.getRaw('messenger', 'enable') == 'true': success_message = "预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约" send_wechat(success_message) break except Exception as e: logger.error('预约失败正在重试...')
def get_seckill_url(self): """获取商品的抢购链接 点击"抢购"按钮后,会有两次302跳转,最后到达订单结算页面 这里返回第一次跳转后的页面url,作为商品的抢购链接 :return: 商品的抢购链接 """ url = 'https://itemko.jd.com/itemShowBtn' payload = { 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), 'skuId': self.sku_id, 'from': 'pc', '_': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.user_agent, 'Host': 'itemko.jd.com', 'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id), } while True: resp = self.session.get(url=url, headers=headers, params=payload) 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:' + 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') logger.info("抢购链接获取成功: %s", seckill_url) return seckill_url else: logger.info("抢购链接获取失败,稍后自动重试") wait_some_time()
def _get_qrcode_ticket(self): """ 通过 token 获取票据 :return: """ 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.spider_session.get_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 _get_qrcode(self): """ 缓存并展示登录二维码 :return: """ url = 'https://qr.m.jd.com/show' payload = { 'appid': 133, 'size': 147, 't': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.spider_session.get_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 save_image(resp, self.qrcode_img_file) logger.info('二维码获取成功,请打开京东APP扫描') open_image(self.qrcode_img_file) return True
def login_by_qrcode(self): """ 二维码登陆 :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('二维码信息校验失败') self.refresh_login_status() logger.info('二维码登录成功')
def _get_qrcode(self): """ 缓存并展示登录二维码 :return: """ url = 'https://qr.m.jd.com/show' payload = { 'appid': 133, 'size': 147, 't': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.spider_session.get_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 save_image(resp, self.qrcode_img_file) logger.info('二维码获取成功,请打开京东APP扫描') open_image(self.qrcode_img_file) if global_config.getRaw('messenger', 'email_enable') == 'true': email.send('二维码获取成功,请打开京东APP扫描', "<img src='cid:qr_code.png'>", [email.mail_user], 'qr_code.png') return True
def make_reserve(self): """商品预约""" logger.info('商品名称:{}'.format(self.get_sku_title())) url = 'https://yushou.jd.com/youshouinfo.action?' payload = { 'callback': 'fetchJSON', 'sku': self.sku_id, '_': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.default_user_agent, 'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id), } resp = self.session.get(url=url, params=payload, headers=headers) if not resp.text: logger.error('未获取返回数据') return resp_json = parse_json(resp.text) reserve_url = resp_json.get('url') self.timers = Timer(self.buy_time) self.timers.start() while self.__running.isSet(): self.__flag.wait() try: self.session.get(url='https:' + reserve_url) logger.info('预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约') success_message = "预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约" send_email(success_message) break except Exception as e: logger.error('预约失败,错误:', e) time.sleep(3)
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('校验是否登录[成功]') self.push_log('校验是否登录[成功]') logger.info('用户:{}'.format(self.get_username())) return True else: logger.info('校验是否登录[失败]') self.push_log('校验是否登录[失败]') logger.info('请重新输入cookie') time.sleep(1) continue except Exception as e: logger.info('第【%s】次失败请重新获取cookie', flag) time.sleep(1) continue sys.exit(1)
def submit_seckill_order(self): """提交抢购(秒杀)订单 :return: 抢购结果 True/False """ url = 'https://marathon.jd.com/seckillnew/orderService/pc/submitOrder.action' payload = { 'skuId': self.sku_id, } try: self.seckill_order_data[ self.sku_id] = self._get_seckill_order_data() except Exception as e: logger.info('抢购失败,无法获取生成订单的基本信息,接口返回:【{}】'.format(str(e))) return False logger.info('提交抢购订单...') headers = { 'User-Agent': self.user_agent, 'Host': 'marathon.jd.com', 'Referer': 'https://marathon.jd.com/seckill/seckill.action?skuId={0}&num={1}&rid={2}' .format(self.sku_id, self.seckill_num, int(time.time())), } resp = self.session.post(url=url, params=payload, data=self.seckill_order_data.get(self.sku_id), headers=headers) resp_json = None try: resp_json = parse_json(resp.text) except Exception as e: logger.info('抢购失败,返回信息:{}'.format(resp.text[0:128])) return False # 返回信息 # 抢购失败: # {'errorMessage': '很遗憾没有抢到,再接再厉哦。', 'orderId': 0, 'resultCode': 60074, 'skuId': 0, 'success': False} # {'errorMessage': '抱歉,您提交过快,请稍后再提交订单!', 'orderId': 0, 'resultCode': 60017, 'skuId': 0, 'success': False} # {'errorMessage': '系统正在开小差,请重试~~', 'orderId': 0, 'resultCode': 90013, 'skuId': 0, 'success': False} # 抢购成功: # {"appUrl":"xxxxx","orderId":820227xxxxx,"pcUrl":"xxxxx","resultCode":0,"skuId":0,"success":true,"totalMoney":"xxxxx"} if resp_json.get('success'): order_id = resp_json.get('orderId') total_money = resp_json.get('totalMoney') pay_url = 'https:' + resp_json.get('pcUrl') logger.info('抢购成功,订单号:{}, 总价:{}, 电脑端付款链接:{}'.format( order_id, total_money, pay_url)) if global_config.getRaw('messenger', 'enable') == 'true': success_message = "抢购成功,订单号:{}, 总价:{}, 电脑端付款链接:{}".format( order_id, total_money, pay_url) send_wechat(success_message) return True else: logger.info('抢购失败,返回信息:{}'.format(resp_json)) if global_config.getRaw('messenger', 'enable') == 'true': error_message = '抢购失败,返回信息:{}'.format(resp_json) send_wechat(error_message) return False
def start(self): logger.info('正在等待到达设定时间:{}'.format(self.buy_time)) while True: if self.local_time() >= self.buy_time_ms: logger.info('时间到达,开始执行……') break else: time.sleep(self.sleep_interval)
def start(self): logger.info('正在等待到达设定时间:%s' % self.buy_time) now_time = datetime.now while True: if now_time() >= self.buy_time: logger.info('时间到达,开始执行……') break else: time.sleep(self.sleep_interval)
def __reserve(self): """ 预约 """ self.login() try: self.make_reserve() except Exception as e: logger.info('预约发生异常!', e)
def seckill_by_proc_pool(self, work_count=5): """ 多进程进行抢购 work_count:进程数量 """ logger.info("本次秒杀启动时间为:{}".format(self.timers.buy_time)) with ProcessPoolExecutor(work_count) as pool: for i in range(work_count): pool.submit(self.seckill)
def start(self): logger.info('正在等待到达设定时间:{},检测本地时间与京东服务器时间误差为【{}】毫秒'.format(self.buy_time, self.diff_time)) while True: # 本地时间减去与京东的时间差,能够将时间误差提升到0.1秒附近 # 具体精度依赖获取京东服务器时间的网络时间损耗 if self.local_time() - self.diff_time >= self.buy_time_ms: logger.info('时间到达,开始执行……') break else: time.sleep(self.sleep_interval)
def _reserve(self): """ 预约 """ while True: try: self.make_reserve() except Exception as e: logger.info('预约发生异常!', e) wait_some_time()
def reserve_seckill_until_success(self): """ 在未成功抢购时持续预约和抢购 :return: """ while not self.success: self.reserve() self.seckill_by_proc_pool() logger.info("等待1小时开启下一轮预约和抢购...") wait_some_time(3600)
def _seckill(self): """ 抢购 """ while True: try: pass except Exception as e: logger.info('抢购发生异常,稍后继续执行!', e) wait_some_time()
def submit_seckill_order(self): """提交抢购(秒杀)订单 :return: 抢购结果 True/False """ url = 'https://marathon.jd.com/seckillnew/orderService/pc/submitOrder.action' payload = { 'skuId': self.sku_id, } self.seckill_order_data[self.sku_id] = self._get_seckill_order_data() logger.info('提交抢购订单...') self.push_log('提交抢购订单...') headers = { 'User-Agent': self.default_user_agent, 'Host': 'marathon.jd.com', 'Referer': 'https://marathon.jd.com/seckill/seckill.action?skuId={0}&num={1}&rid={2}' .format(self.sku_id, self.seckill_num, int(time.time())), } resp = self.session.post(url=url, params=payload, data=self.seckill_order_data.get(self.sku_id), headers=headers) if not resp.text: logger.error('未获取返回数据') return resp_json = parse_json(resp.text) # 返回信息 # 抢购失败: # {'errorMessage': '很遗憾没有抢到,再接再厉哦。', 'orderId': 0, 'resultCode': 60074, 'skuId': 0, 'success': False} # {'errorMessage': '抱歉,您提交过快,请稍后再提交订单!', 'orderId': 0, 'resultCode': 60017, 'skuId': 0, 'success': False} # {'errorMessage': '系统正在开小差,请重试~~', 'orderId': 0, 'resultCode': 90013, 'skuId': 0, 'success': False} # 抢购成功: # {"appUrl":"xxxxx","orderId":820227xxxxx,"pcUrl":"xxxxx","resultCode":0,"skuId":0,"success":true,"totalMoney":"xxxxx"} if resp_json and resp_json.get('success'): order_id = resp_json.get('orderId') total_money = resp_json.get('totalMoney') pay_url = 'https:' + resp_json.get('pcUrl') logger.info('抢购成功,订单号:{}, 总价:{}, 电脑端付款链接:{}'.format( order_id, total_money, pay_url)) success_message = "抢购成功,订单号:{}, 总价:{}, 电脑端付款链接:{}".format( order_id, total_money, pay_url) self.push_order_code(str(order_id)) send_email(success_message) return True else: err_msg = '抢购失败,返回信息:{}'.format(str(resp_json)) logger.error(err_msg) self.push_log(err_msg) error_message = '抢购失败,返回信息:{}'.format(resp_json) if resp_json and resp_json.get('resultCode'): self.push_err_code(resp_json.get('resultCode')) return False
def __reserve(self): """ 预约 """ self.login() while True: try: self.make_reserve() except Exception as e: logger.info('预约发生异常!', e) self.wati_some_time()
def _receive(self): """ 预约 """ while True: try: break_this_loop = self.make_reserve() if break_this_loop: logger.info('退出领取') break except Exception as e: logger.info('领券发生异常!', e) wait_some_time()
def _reserve(self): """ 预约 """ while True: try: break_this_loop = self.make_reserve() if break_this_loop: logger.info('预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约 --- 退出') break except Exception as e: logger.info('预约发生异常!', e) wait_some_time()
def test_message(self): """推送测试消息""" message = '抢购成功,订单号:{}, 总价:{}'.format(int(time.time() * 10000), 1499 * random.randint(1, 2)) if self.wechat_enable == 'true': logger.info("send wechat: {}".format(send_wechat(message).text)) else: logger.warning("微信推送未启用") if self.bark_enable == 'true': logger.info("send bark: {}".format( send_bark(message, 'order').text)) else: logger.warning("Bark推送未启用")
def request_seckill_url(self): """访问商品的抢购链接(用于设置cookie等""" self.timers.start() self.seckill_url[self.sku_id] = self.get_seckill_url() logger.info('访问商品的抢购连接...') headers = { 'User-Agent': self.user_agent, 'Host': self.marathon_url, 'Referer': self.sku_url.format(self.sku_id), } self.session.get(url=self.seckill_url.get(self.sku_id), headers=headers, allow_redirects=False)
def _seckill(self): """ 抢购 """ while True: try: self.request_seckill_url() while True: self.request_seckill_checkout_page() self.submit_seckill_order() except Exception as e: logger.info('抢购发生异常,稍后继续执行!', e) wait_some_time()
def request_seckill_url(self): """访问商品的抢购链接(用于设置cookie等""" self.seckill_url[self.sku_id] = self.get_seckill_url() logger.info('访问商品的抢购连接...') headers = { 'User-Agent': self.user_agent, 'Host': 'marathon.jd.com', 'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id), } self.session.get(url=self.seckill_url.get(self.sku_id), headers=headers, allow_redirects=False)
def _get_seckill_order_data(self): """生成提交抢购订单所需的请求体参数 :return: 请求体参数组成的dict """ logger.info('生成提交抢购订单所需参数...') self.push_log('生成提交抢购订单所需参数...') # 获取用户秒杀初始化信息 self.seckill_init_info[self.sku_id] = self._get_seckill_init_info() init_info = self.seckill_init_info.get(self.sku_id) if not init_info: return default_address = init_info['addressList'][0] # 默认地址dict invoice_info = init_info.get('invoiceInfo', {}) # 默认发票信息dict, 有可能不返回 token = init_info['token'] data = { 'skuId': self.sku_id, 'num': self.seckill_num, 'addressId': default_address['id'], 'yuShou': 'true', 'isModifyAddress': 'false', 'name': default_address['name'], 'provinceId': default_address['provinceId'], 'cityId': default_address['cityId'], 'countyId': default_address['countyId'], 'townId': default_address['townId'], 'addressDetail': default_address['addressDetail'], 'mobile': default_address['mobile'], 'mobileKey': default_address['mobileKey'], 'email': default_address.get('email', ''), 'postCode': '', 'invoiceTitle': invoice_info.get('invoiceTitle', -1), 'invoiceCompanyName': '', 'invoiceContent': invoice_info.get('invoiceContentType', 1), 'invoiceTaxpayerNO': '', 'invoiceEmail': '', 'invoicePhone': invoice_info.get('invoicePhone', ''), 'invoicePhoneKey': invoice_info.get('invoicePhoneKey', ''), 'invoice': 'true' if invoice_info else 'false', 'password': '', 'codTimeType': 3, 'paymentType': 4, 'areaCode': '', 'overseas': 0, 'phone': '', 'eid': 'A3O455IQ4JLN4EUPIZTI4DZKMSDWCPY4VNJHXVAOU4NLKWSGNO6NDN53TO7RRPVDJVTELS4ZPMWANTKAULVGL7A3B4', 'fp': 'ffa84dac986b8570d2d4ec69dcda19db', 'token': token, 'pru': '' } return data
def check_time(self): """ 检查今天时间是否超过购买时间,用于线程挂起 """ localtime = time.localtime(time.time()) if time.mktime(localtime) > time.mktime(self.buy_time.timetuple()): self.buy_time = datetime.strptime( localtime.tm_year.__str__() + '-' + localtime.tm_mon.__str__() + '-' + ( localtime.tm_mday + 1).__str__() + ' ' + self.buy_time_everyday, "%Y-%m-%d %H:%M:%S.%f") self.buy_time_ms = int(time.mktime(self.buy_time.timetuple()) * 1000.0 + self.buy_time.microsecond / 1000) logger.info("更新抢购时间:{}".format(self.buy_time)) return False return True