class LrtsLogin: def __init__(self, username: str = None, password: str = None): self.site = 'lrts' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest' } # 密码错误重置 self.reset_flag = False def check_islogin(self, cookies): res = self.session.get('http://www.lrts.me/index', cookies=cookies) html = res.content.decode(chardet.detect(res.content)['encoding']) bsobj = BeautifulSoup(html, 'lxml') if bsobj.select('h4.nowrap a'): nickname = bsobj.select('h4.nowrap a')[0].get_text() self.logger.info('登录成功!') self.logger.info('Hello, {}!'.format(nickname)) return True return False def _get_token(self): url = 'http://www.lrts.me/user/login_token.do' data = {'accountName': self.username} res = self.session.post(url, data=data).json() return res['data'] def _encrypt_pwd(self, token): with open('encrypt.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) return ctx.call('encrypt_pwd', self.password, token) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): api = 'http://www.lrts.me/user/login.do' token = self._get_token() pwd = self._encrypt_pwd(token) data = { 'accountName': self.username, 'hashPass': pwd, 'autoLogin': '******', 'validateCode': '' } res = self.session.post(api, data=data) cookies = res.cookies.get_dict() if self.check_islogin(cookies): self.logger.info('Cookies 有效期: {} 天'.format( int(res.json()['data']['expires'] / 86400))) self.redis_client.save_cookies(self.site, self.username, cookies) return cookies elif res.json()['errMsg'] == '帐号或密码错误': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: ', res.json()['errMsg']) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login()
class ZhipinLogin: def __init__(self, username: str = None, password: str = None): self.site = 'zhipin' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) async def check_islogin(self, cookies): """ 检查登录状态: 访问首页, 出现用户名则登录成功 :param page: :return: """ url = 'https://www.zhipin.com/geek/new/index/recommend?ka=header-personal' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299' } res = requests.get(url, headers=headers, cookies=cookies) if '登录' not in res.text: self.logger.info('Cookies 有效! ') nickname = re.search('name:"(.*?)"', res.text).group(1) self.logger.info('Hello, {}! '.format(nickname)) return True return False @staticmethod def input_time_random(): return random.randint(150, 201) def retry_if_result_none(self, result): self.logger.warning('滑块判定失败, 重试!') return result is None async def login(self): browser = await launch( { 'headless': False, 'args': ['--no-sandbox', '--disable-infobars'], }, # userDataDir=r'D:\login\userdata', # 这个文件会记录pyppeteer浏览器的cookie args=['--window-size=1366, 768'] ) page = await browser.newPage() # 启动个新的浏览器页面 await page.setJavaScriptEnabled(enabled=True) # 启用js await page.setUserAgent( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299' ) # 设置模拟浏览器 self.logger.info('尝试登录...') await page.goto('https://www.zhipin.com/user/login.html') await self.page_evaluate(page) time.sleep(2) time.sleep(1) await page.type('input[name="account"]', self.username, {'delay': self.input_time_random() - 50}) time.sleep(1) await page.type('input[name="password"]', self.password, {'delay': self.input_time_random()}) # await page.screenshot({'path': './headless-test-result.png'}) # 截图测试 time.sleep(2) self.logger.info('拉动滑块验证...') # await page.screenshot({'path': './headless-login-slide.png'}) # 截图测试 flag = await self.mouse_slide(page=page) # js拉动滑块 if flag: # await page.keyboard.press('Enter') await page.click('button.btn') await page.waitFor(20) await page.waitForNavigation() # 等待跳转 try: global error error = await page.Jeval('.dialog-con', 'node => node.textContent') # 检测是否是账号密码错误 except Exception as e: error = None self.logger.info("登录成功! ") finally: if error: error = await (await (await page.xpath('//div[@class="dialog-con"]'))[0].getProperty('textContent')).jsonValue() self.logger.info(error) else: await asyncio.sleep(3) cookies = await page.cookies() # cookies = {item['name']: item['value'] for item in cookies} self.redis_client.save_cookies(self.site, self.username, cookies) else: self.logger.error('验证失败! ') await self.page_close(browser) async def page_evaluate(self, page): await page.evaluate( '''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => undefined } }) }''') # 以下为插入中间js,将淘宝会为了检测浏览器而调用的js修改其结果。 await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {}, }; }''') await page.evaluate( '''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''') await page.evaluate( '''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''') async def page_close(self, browser): """ 关闭浏览器驱动 :param browser: :return: """ for _page in await browser.pages(): await _page.close() await browser.close() # @retry(retry_on_result=retry_if_result_none, ) async def mouse_slide(self, page=None): await asyncio.sleep(3) try: # 鼠标移动到滑块,按下,滑动到头(然后延时处理),松开按键 await page.hover('.btn_slide') await page.mouse.down() # 模拟按下鼠标 await page.mouse.move(2000, 0, {'delay': random.randint(1000, 2000)}) # js模拟拖动 await page.mouse.up() # 模拟松开鼠标 except Exception as e: return None, page else: await asyncio.sleep(2) slider_again = await page.Jeval('.nc-lang-cnt', 'node => node.textContent') # 判断是否通过 if slider_again != '验证通过': return None, page else: # await page.screenshot({'path': './headless-slide-result.png'}) # 截图测试 self.logger.info('验证通过! ') return 1, page @check_user() async def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) cookies = {item['name']: item['value'] for item in cookies} if cookies: if await self.check_islogin(cookies): return True self.logger.warning('cookies 已过期! ') await self.login()
class LOLLogin: def __init__(self, username: str = None, password: str = None): self.site = 'lol' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Connection': 'keep-alive', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0' } # 密码错误重置初始化 self.reset_flag = False with open('encrypt.js', 'rb') as f: js = f.read().decode() self.ctx = execjs.compile(js) def check_islogin(self, cookies): params = { 'use': 'zm,uid,acc', 'area': '19', 'season': 's9', 'callback': 'jQuery191005632563829884707_{}'.format(int(time.time() * 1000)), '_': int(time.time() * 1000) } url = 'https://lol.ams.game.qq.com/lol/autocms/v1/transit/LOL/LOLWeb/Official/MobilePlayerInfo,PlayerCommunityInfo,PlayerInfo,PlayerBattleSummary,PlayerHonor,PlayerProperty,PlayerRankInfo?' + urlencode( params) res = self.session.get(url, cookies=cookies) result = json.loads( re.search(r'{}\((.*?)\)'.format(params['callback']), res.text).group(1)) if result['MobilePlayerInfo']['status'] == 0: self.logger.info('Cookies 有效! ') nickname = result['MobilePlayerInfo']['msg']['res'][ 'uuid_prifle_list'][0]['nick'] self.logger.info('Hello, {}! '.format(nickname)) gamename = result['PlayerInfo']['msg']['name'] self.logger.info('你的游戏昵称: {}'.format(gamename)) self.logger.info('查看战绩请按 1, 结束请按 0 >>') flag = int(input()) if flag: seasons = result['PlayerBattleSummary']['msg']['data'][ 'item_list'] for season in seasons: self.logger.info(season) return True return True return False def _init_cookies(self): self.session.get('https://mail.qq.com/') def _get_login_sig(self): res = self.session.get( 'https://xui.ptlogin2.qq.com/cgi-bin/xlogin?proxy_url=https://game.qq.com/comm-htdocs/milo/proxy.html&appid=21000501&target=top&s_url=https%3A%2F%2Flol.qq.com%2Fmain.shtml&style=20&daid=8' ) cookies = res.cookies.get_dict() login_sig = cookies['pt_login_sig'] return cookies, login_sig def _get_salt(self, login_sig): url = 'https://ssl.ptlogin2.qq.com/check?' params = { 'regmaster': '', 'pt_tea': '2', 'pt_vcode': '1', 'uin': self.username, 'appid': 21000501, 'js_ver': '19072517', 'js_type': 1, 'login_sig': login_sig, 'u1': 'https://lol.qq.com/main.shtml', 'r': self.ctx.call('get_random_num'), 'pt_uistyle': '40' } res = self.session.get(url, params=params) pt_verifysession_v1 = res.text.split(',')[3].replace("'", '') verify_code = res.text.split(',')[1].replace("'", '') salt = res.text.split(',')[2].replace("'", '') salt = salt.encode().decode('unicode_escape') ptdrvs = res.text.split(',')[5].replace("'", '').replace(')', '').strip() return pt_verifysession_v1, verify_code, salt, ptdrvs def _encrypt_pwd(self, salt, verify_code): return self.ctx.call('encrypt_pwd', self.password, salt, verify_code) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): self._init_cookies() login_api = 'https://ssl.ptlogin2.qq.com/login?' login_sig = self._get_login_sig() pt_verifysession_v1, verify_code, salt, ptdrvs = self._get_salt( login_sig) pwd = self._encrypt_pwd(salt, verify_code) params = { 'u': self.username, 'verifycode': verify_code, 'pt_vcode_v1': '0', 'pt_verifysession_v1': pt_verifysession_v1, 'p': pwd, 'pt_randsalt': '2', 'u1': 'https://lol.qq.com/main.shtml', 'ptredirect': '1', 'h': '1', 't': '1', 'g': '1', 'from_ui': '1', 'ptlang': '2052', 'action': f'1-0-{int(time.time() * 1000)}', 'js_ver': '19072517', 'js_type': '1', 'login_sig': login_sig, 'pt_uistyle': '40', 'aid': 21000501, 'daid': '8', 'ptdrvs': ptdrvs, '': '' } res = self.session.get(login_api, params=params) result = re.search(r'ptuiCB\((.*?)\)', res.text).group(1).replace("'", '') result = result.split(',') if result[0] == '0': url = result[2] resp = self.session.get(url, allow_redirects=False) if resp.status_code == 302 and resp.headers[ 'location'] == 'https://lol.qq.com/main.shtml': self.logger.info('登录成功! ') nickname = result[-1] self.logger.info('Hello, {}! '.format(nickname)) cookies = resp.cookies.get_dict() self.redis_client.save_cookies(self.site, self.username, cookies) return True raise Exception('登录失败! ') elif '密码不正确' in result[-2]: self.reset_flag = True raise Exception('账号或密码错误! ') elif '二维码登录' in res.text: self.logger.warning('为了更好的保护您的QQ,请使用扫描二维码登录! ') return False raise Exception('登录失败: {} '.format(result[-2])) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期') self.login()
class IQiyiLogin: def __init__(self, username: str = None, password: str = None): self.site = 'iqiyi' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Origin': 'http://www.iqiyi.com', 'Referer': 'http://www.iqiyi.com/iframe/loginreg?ver=1', "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36" } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): url = 'http://www.iqiyi.com/u/point' res = self.session.get(url, cookies=cookies) if 'ucbannerName' in res.text: self.logger.info('Cookies 有效! ') bsobj = BeautifulSoup(res.text, 'lxml') nickname = bsobj.select('#ucbannerName')[0].get_text().strip() self.logger.info('Hello, {}! '.format(nickname)) return True return False def _encrypt_pwd(self): """ 加密密码 :return: """ with open('iqiyiPwdEncrypt.js', 'r') as f: js = f.read() ctx = execjs.compile(js) return ctx.call('rsaFun', self.password) def _get_areacode(self): """ 获取地区编码 :return: """ url = 'https://passport.iqiyi.com/apis/phone/get_support_areacode.action' data = { 'use_case': '1', 'local': '1', 'agenttype': '1', 'fromSDK': '1', 'ptid': '01010021010000000000', 'sdk_version': '1.0.0' } r = self.session.post(url, data=data).json() code_dict = r['data']['acode'] return code_dict def get_dfp(self): """ 获取页面初始化参数 dfp :return: """ url = 'https://cook.iqiyi.com/security/dfp_pcw/sign' data = { 'dim': 'eyJqbiI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS83NS4wLjM3NzAuODAgU2FmYXJpLzUzNy4zNiIsImNtIjoiemgtQ04iLCJndSI6MjQsInVmIjoxLCJqciI6WzEzNjYsNzY4XSwiZGkiOlsxMzY2LDcyOF0sInpwIjotNDgwLCJ1aCI6MSwic2giOjEsImhlIjoxLCJ6byI6MSwicnYiOiJ1bmtub3duIiwibngiOiJXaW4zMiIsIml3IjoidW5rbm93biIsInFtIjpbIkNocm9tZSBQREYgUGx1Z2luOjpQb3J0YWJsZSBEb2N1bWVudCBGb3JtYXQ6OmFwcGxpY2F0aW9uL3gtZ29vZ2xlLWNocm9tZS1wZGZ cGRmIiwiQ2hyb21lIFBERiBWaWV3ZXI6Ojo6YXBwbGljYXRpb24vcGRmfnBkZiIsIk5hdGl2ZSBDbGllbnQ6Ojo6YXBwbGljYXRpb24veC1uYWNsfixhcHBsaWNhdGlvbi94LXBuYWNsfiJdLCJ3ciI6ImI3NzY2NGM3MTcwNzdhZmZmMzNhN2QyODM2ZTIzNzdjIiwid2ciOiJlZDI2NTg5MTM1MTJlNTA5MmZlMjE5NDAwOGQ3OWEwZSIsImZrIjpmYWxzZSwicmciOmZhbHNlLCJ4eSI6ZmFsc2UsImptIjpmYWxzZSwiYmEiOmZhbHNlLCJ0bSI6WzAsZmFsc2UsZmFsc2VdLCJhdSI6dHJ1ZSwibWkiOiI5YmU0OTM0MS05MTI2LTg5MjQtNjc2Ni0xOTA3Y2QxNTYxMDgiLCJjbCI6IlBDV0VCIiwic3YiOiIxLjAiLCJqZyI6IjA3NjQ4M2QwODg2NGMzNTE5MWUyNTVjNjhmNWU2YWE3IiwiZmgiOiJvazZxeWc0cXM5YWw5MTA0YjZ0OTJoODEiLCJpZm0iOltmYWxzZSxudWxsLG51bGwsbnVsbF0sImV4IjoiIiwiZHYiOiJvZmYiLCJwdiI6dHJ1ZX0=', 'plat': 'PCWEB', 'ver': '1.0', 'sig': '785042546DC84608E1DF7430A9AE021C2F6F7955', 'nifc': 'false' } result = self.session.post(url, data=data).json() if result['code'] == 0: dfp = result['result']['dfp'] print('dfp: {}'.format(dfp)) expire_time = time.strftime( "%H:%M:%S", time.localtime(result['result']['expireAt'] - int(time.time() * 1000))) print('{} 后过期'.format(expire_time)) return dfp return None @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 登录, 只有一个加密参数密码, 其他为定值 :return: """ url = 'https://passport.iqiyi.com/apis/reglogin/login.action' encrypt_pwd = self._encrypt_pwd() dfp = self.get_dfp() data = { 'email': self.username, 'fromSDK': '1', 'sdk_version': '1.0.0', 'passwd': encrypt_pwd, 'agenttype': '1', '__NEW': '1', 'checkExist': '1', 'lang': '', 'ptid': '01010021010000000000', 'nr': '1', 'verifyPhone': '1', 'area_code': '86', # 'env_token': 'c225a3e03fdf4c90a9227af2f0abd8bb', 'dfp': dfp, 'envinfo': 'eyJqbiI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS83NS4wLjM3NzAuODAgU2FmYXJpLzUzNy4zNiIsImNtIjoiemgtQ04iLCJndSI6MjQsInVmIjoxLCJqciI6WzEzNjYsNzY4XSwiZGkiOlsxMzY2LDcyOF0sInpwIjotNDgwLCJ1aCI6MSwic2giOjEsImhlIjoxLCJ6byI6MSwicnYiOiJ1bmtub3duIiwibngiOiJXaW4zMiIsIml3IjoidW5rbm93biIsInFtIjpbIkNocm9tZSBQREYgUGx1Z2luOjpQb3J0YWJsZSBEb2N1bWVudCBGb3JtYXQ6OmFwcGxpY2F0aW9uL3gtZ29vZ2xlLWNocm9tZS1wZGZ cGRmIiwiQ2hyb21lIFBERiBWaWV3ZXI6Ojo6YXBwbGljYXRpb24vcGRmfnBkZiIsIk5hdGl2ZSBDbGllbnQ6Ojo6YXBwbGljYXRpb24veC1uYWNsfixhcHBsaWNhdGlvbi94LXBuYWNsfiJdLCJ3ciI6ImI3NzY2NGM3MTcwNzdhZmZmMzNhN2QyODM2ZTIzNzdjIiwid2ciOiJlZDI2NTg5MTM1MTJlNTA5MmZlMjE5NDAwOGQ3OWEwZSIsImZrIjpmYWxzZSwicmciOmZhbHNlLCJ4eSI6ZmFsc2UsImptIjpmYWxzZSwiYmEiOmZhbHNlLCJ0bSI6WzAsZmFsc2UsZmFsc2VdLCJhdSI6dHJ1ZSwibWkiOiI5YmU0OTM0MS05MTI2LTg5MjQtNjc2Ni0xOTA3Y2QxNTYxMDgiLCJjbCI6IlBDV0VCIiwic3YiOiIxLjAiLCJqZyI6IjA3NjQ4M2QwODg2NGMzNTE5MWUyNTVjNjhmNWU2YWE3IiwiZmgiOiJvazZxeWc0cXM5YWw5MTA0YjZ0OTJoODEiLCJpZm0iOltmYWxzZSxudWxsLG51bGwsbnVsbF0sImV4IjoiIiwiZHYiOiJvZmYiLCJwdiI6dHJ1ZX0=' } while True: res = self.session.post(url, data=data) cookies = res.cookies.get_dict() if res.json()['code'] == 'A00000': self.logger.info('登录成功! ') self.logger.info('Hello, {}! '.format( res.json()['data']['nickname'])) self.redis_client.save_cookies(self.site, self.username, cookies) return cookies elif res.json()['code'] == 'P00117': self.reset_flag = True raise Exception('账号或密码错误! ') token = res.json()['data']['data']['token'] data.update({'env_token': token}) self.logger.info(f'{res.json()["msg"]}, env_token更新为: {token}') time.sleep(random.random()) def get_userinfo(self, cookies): """ 获取用户信息 :return: """ url = 'https://passport.iqiyi.com/apis/user/info.action' code_dict = self._get_areacode() with open('antiCsrf.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) try: auth_cookie = cookies['P00001'] anticsrf = ctx.call('getAnticsrf', auth_cookie) data = { 'agenttype': '1', 'ptid': '01010021010000000000', 'lang': '', 'fromSDK': '1', 'sdk_version': '1.0.0', 'authcookie': auth_cookie, 'antiCsrf': anticsrf, 'fields': 'insecure_account,userinfo' } r = requests.post(url, data=data) user_info = r.json()['data']['userinfo'] jointime = time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime(int(user_info['jointime']))) if user_info['gender'] == 1: gender = '男' else: gender = '女' area_code = user_info['area_code'] for code in code_dict.keys(): if area_code == code: area = code_dict[code] if user_info['birthday']: birthday = time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime(int(user_info['birthday']))) user_info.update({ 'birthday': birthday, 'jointime': jointime, 'gender': gender, 'area': area }) # 移除地区代码 del user_info['area_code'] self.logger.info(f'您的基本信息为: {user_info}') return True except: return False @check_user() def run(self, load_cookies: bool = True): """ 主函数 :return: """ if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): info_flag = input('是否显示用户信息? (yes/no) \n') if info_flag == 'yes': self.get_userinfo(cookies) return cookies self.logger.warning('Cookies 已过期') return self.login()
class IQiyiLogin: def __init__(self, username: str = None, password: str = None): self.site = 'iqiyi' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Origin': 'http://www.iqiyi.com', 'Referer': 'http://www.iqiyi.com/iframe/loginreg?ver=1', "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36" } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): url = 'http://www.iqiyi.com/u/point' res = self.session.get(url, cookies=cookies) if 'ucbannerName' in res.text: self.logger.info('Cookies 有效! ') bsobj = BeautifulSoup(res.text, 'lxml') nickname = bsobj.select('#ucbannerName')[0].get_text().strip() self.logger.info('Hello, {}! '.format(nickname)) return True return False def _encrypt_pwd(self): """ 加密密码 :return: """ with open('iqiyiPwdEncrypt.js', 'r') as f: js = f.read() ctx = execjs.compile(js) return ctx.call('rsaFun', self.password) def _get_areacode(self): """ 获取地区编码 :return: """ url = 'https://passport.iqiyi.com/apis/phone/get_support_areacode.action' data = { 'use_case': '1', 'local': '1', 'agenttype': '1', 'fromSDK': '1', 'ptid': '01010021010000000000', 'sdk_version': '1.0.0' } r = self.session.post(url, data=data).json() code_dict = r['data']['acode'] return code_dict @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 登录, 只有一个加密参数密码, 其他为定值 :return: """ url = 'https://passport.iqiyi.com/apis/reglogin/login.action' encrypt_pwd = self._encrypt_pwd() data = { 'email': self.username, 'fromSDK': '1', 'sdk_version': '1.0.0', 'passwd': encrypt_pwd, 'agenttype': '1', '__NEW': '1', 'checkExist': '1', 'lang': '', 'ptid': '01010021010000000000', 'nr': '1', 'verifyPhone': '1', 'area_code': '86', 'env_token': 'c225a3e03fdf4c90a9227af2f0abd8bb', 'dfp': 'a06e54c2dfe5d24ebf8aec1c2d0a8f5afb2fc70fd2a147f7ab9e5aea7cff440f9e', 'envinfo': 'eyJqbiI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdPVzY0OyBydjo2Ny4wKSBHZWNrby8yMDEwMDEwMSBGaXJlZm94LzY3LjAiLCJjbSI6InpoLUNOIiwiZ3UiOjI0LCJ1ZiI6MSwianIiOlsxMzY2LDc2OF0sImRpIjpbMTM2Niw3MjhdLCJ6cCI6LTQ4MCwidWgiOjEsInNoIjoxLCJoZSI6MSwicnYiOiJ1bmtub3duIiwibngiOiJXaW4zMiIsIml3IjoidW5zcGVjaWZpZWQiLCJxbSI6W10sIndyIjoiOWUzYjk5MzFhNzBiMGQwZDI0NGU1ZTg1MTAyZGJiYTAiLCJ3ZyI6IjRlMzVhYWVjZTM2NTU0YTM5MGQwYWU1MDNlZDljOTM0IiwiZmsiOmZhbHNlLCJyZyI6ZmFsc2UsInh5IjpmYWxzZSwiam0iOmZhbHNlLCJiYSI6ZmFsc2UsInRtIjpbMCxmYWxzZSxmYWxzZV0sImF1Ijp0cnVlLCJtaSI6IjQ1MjY1MDUxLWM3MjItNTcyOC00OGY3LWJiMjA3N2NlMGVhMCIsImNsIjoiUENXRUIiLCJzdiI6IjEuMCIsImpnIjoiNTE3YzNiNDk0NzFlMTJiZTc0N2QzYWI3MWY1YTM1OTMiLCJmaCI6ImhydWtvdjY0OW15OGV1YnB4Ym9uMThuayIsImlmbSI6W3RydWUsNDYwLDQyMCwiaHR0cDovL3d3dy5pcWl5aS5jb20vIl0sImV4IjoiIiwicHYiOmZhbHNlfQ==' } while True: res = self.session.post(url, data=data) cookies = res.cookies.get_dict() if res.json()['code'] == 'A00000': self.logger.info('登录成功! ') self.logger.info('Hello, {}! '.format(res.json()['data']['nickname'])) self.redis_client.save_cookies(self.site, self.username, cookies) return True elif res.json()['code'] == 'P00117': self.reset_flag = True raise Exception('账号或密码错误! ') token = res.json()['data']['data']['token'] data.update({'env_token': token}) self.logger.info(f'{res.json()["msg"]}, env_token更新为: {token}') time.sleep(random.random()) def get_userinfo(self, cookies): """ 获取用户信息 :return: """ url = 'https://passport.iqiyi.com/apis/user/info.action' code_dict = self._get_areacode() with open('antiCsrf.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) try: auth_cookie = cookies['P00001'] anticsrf = ctx.call('getAnticsrf', auth_cookie) data = { 'agenttype': '1', 'ptid': '01010021010000000000', 'lang': '', 'fromSDK': '1', 'sdk_version': '1.0.0', 'authcookie': auth_cookie, 'antiCsrf': anticsrf, 'fields': 'insecure_account,userinfo' } r = requests.post(url, data=data) user_info = r.json()['data']['userinfo'] jointime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(user_info['jointime']))) if user_info['gender'] == 1: gender = '男' else: gender = '女' area_code = user_info['area_code'] for code in code_dict.keys(): if area_code == code: area = code_dict[code] if user_info['birthday']: birthday = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(user_info['birthday']))) user_info.update({'birthday': birthday, 'jointime': jointime, 'gender': gender, 'area': area}) # 移除地区代码 del user_info['area_code'] self.logger.info(f'您的基本信息为: {user_info}') return True except: return False @check_user() def run(self, load_cookies: bool = True): """ 主函数 :return: """ if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): info_flag = input('是否显示用户信息? (yes/no) \n') if info_flag == 'yes': self.get_userinfo(cookies) return True self.logger.warning('Cookies 已过期') self.login()
class Email163Login: def __init__(self, username: str = None, password: str = None): self.site = '163email' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Content-Type': 'application/json', 'Origin': 'https://dl.reg.163.com', 'Referer': 'https://dl.reg.163.com/webzj/v1.0.1/pub/index_dl2_new.html?cd=https%3A%2F%2Fmimg.127.net%2Fp%2Ffreemail%2Findex%2Funified%2Fstatic%2F2019%2Fcss%2F&cf=urs.163.bc0e7491.css&MGID=1561887066637.6414&wdaId=&pkid=CvViHzl&product=mail163', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36', } with open('encryptEnvinfo.js', 'r') as f: self.js = f.read() self.ctx = execjs.compile(self.js) self.rtid = self.ctx.call('getRtid') # 密码错误重试初始化 self.reset_flag = False def check_islogin(self, cookies): sid = cookies['sid'] params = {'sid': sid} cookie = cookies['cookies'] resp = self.session.get('http://mail.163.com/js6/main.jsp?', params=params, cookies=cookie) if '收件箱' in resp.text: self.logger.info('Cookies 有效! ') nickname = re.search("'true_name':'(.*?)'", resp.text).group(1) self.logger.info('Hello, {}! '.format(nickname)) return True return False def _set_data(self, func): """ 配置收件箱接口表单参数 :return: """ with open('data.html', 'r') as f: html = f.read() data_list = html.split('<?xml version="1.0"?>') if func == 'global:sequential': return data_list[1] elif func == 'mbox:listMessages': return data_list[2] elif func == 'mbox:readMessage': return data_list[3] else: self.logger.warning('不支持的接口类型, 有需要请自行添加! ') @staticmethod def _format_date(text): """ 格式化日期 :param date: :return: """ date = re.search(r"new Date\((.*?)\)", text, re.S) send_time = '-'.join([ x for x in date.group(1).split(',')[:3] ]) + ' ' + ':'.join([x for x in date.group(1).split(',')[3:]]) return text.replace(date.group(0), send_time) def _read_email(self, cookies): """ 查看收件箱: 接口返回的数据是字典样式的原生字符串, demjson 格式化后都无法转为 json 格式... 懒得用正则提取了, 将就看 :return: """ cookie = cookies['cookies'] sid = cookies['sid'] self.session.headers.update({ 'Accept': 'text/javascript', # 请求头携带这个参数接口返回的是格式化数据, 没有这个参数返回的html源码 'Content-type': 'application/x-www-form-urlencoded' }) url = 'https://mail.163.com/js6/s?' func_map = { '1': 'global:sequential', '2': 'mbox:listMessages', '3': 'mbox:readMessage' } sign_list = [] all_flag = input('是否查看邮箱整体信息? (yes/任意键跳过) >> \n') if all_flag == 'yes': all_flag = 1 else: all_flag = 0 if all_flag: sign_list.append('1') mailbox_flag = input('是否查看收件箱邮件列表? (yes/任意键跳过) >> \n') if mailbox_flag == 'yes': mailbox_flag = 1 else: mailbox_flag = 0 if mailbox_flag: sign_list.append('2') for sign in sign_list: params = { 'sid': sid, 'func': func_map[sign], } data = {'var': self._set_data(func_map[sign])} res = self.session.post(url, params=params, data=data, cookies=cookie) if sign == '1': self.logger.info('邮箱整体信息如下:') pprint(res.text) elif sign == '2': self.logger.info('收件箱邮件列表: ') ids = re.findall("'id':'(.*?)'", res.text) subjects = re.findall("'subject':'(.*?)'", res.text) id_dict = {str(index): id_ for index, id_ in enumerate(ids)} subject_dict = { str(index): subject for index, subject in enumerate(subjects) } if id_dict: pprint(subject_dict) view_flag = input('是否需要查看具体邮件信息? (yes/任意键退出) >> \n') if view_flag == 'yes': view_flag = 1 else: view_flag = 0 while view_flag: email_num = input('请输入邮件编号(左边数字)>> ') params = { 'sid': sid, 'func': func_map['3'], } origin_id = re.search('"id">(.*?)<', self._set_data( func_map['3'])).group(1) data = { 'var': self._set_data(func_map['3']).replace( origin_id, id_dict[email_num]) } resp = self.session.post(url, params=params, data=data, cookies=cookie) result = self._format_date(resp.text) pprint(result) view_flag = int(input('继续查看请按 1 , 退出程序请按 0 >> \n')) else: self.logger.info('收件箱列表空空如也~ ') def _init_cookies(self): """ 初始化 Cookies, 关键 Cookie: l_s_mail163CvViHzl, 有这个 Cookie 拿到 tk :return: """ url = 'https://dl.reg.163.com/dl/ini?' params = { "pd": "mail163", "pkid": "CvViHzl", "pkht": "mail.163.com", "channel": "0", "topURL": "https://mail.163.com/", "rtid": self.rtid, "nocache": int(time.time() * 1000) } self.session.get(url, params=params) def _get_token(self): url = 'https://dl.reg.163.com/gt?' params = { 'un': self.username, 'pkid': 'CvViHzl', 'pd': 'mail163', 'channel': 0, 'topURL': 'https://mail.163.com/', 'rtid': self.rtid, 'nocache': int(time.time() * 1000) } res = self.session.get(url, params=params).json() token = res['tk'] return token def _encrypt_pwd(self): """ 加密密码 :return: """ return self.ctx.call('encrypt2', self.password) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): self._init_cookies() tk = self._get_token() login_api = 'https://dl.reg.163.com/dl/l' data = { "un": self.username, "pw": self._encrypt_pwd(), "pd": "mail163", "l": 0, "channel": 0, "d": 10, "t": int(time.time() * 1000), "pkid": "CvViHzl", "domains": "", "tk": tk, "pwdKeyUp": 1, "topURL": "https://mail.163.com/", "rtid": self.rtid } self.session.headers.update({ 'Cookie': 'JSESSIONID-WYTXZDL=YuenayJO3PfX%2BYyPmO%2Fhr%2FLeuu67nXDZPl50EOxAi9plAsq%5CKwf%5CZtQUUxOLA%2FxMlf%2FfxzPNLbTGxZaHUPTny%2BehaVqiukV7ed%2Fblp101N%2BGIIGYKZ9ODzLdR5w5eK%5ChGHO%2FFs2z4umi4Wj2xDzvxbm%2F8lTJBlIU6b9bF0FJrdn1%2Be%2Fq%3A1565361595090; l_s_mail163CvViHzl=2BDA1093FDDA9283AD02B57FFFEC7E0E75F576DC05D86CE6F6B6F9518A920CC1446CE99EF410B468FEDA27F8B53F2F93244D4991F985FACFD8D854C5298024A35F4A702F5D8A93285EB127782B2C254290B1202774ECFD0CE880601326AA4B229FF48A285C3118029255EAE3F260AA5A' }) res = self.session.post(login_api, data=json.dumps(data)).json() if res['ret'] == '201': form_data = { "style": "-1", "df": "mail163_letter", "allssl": "true", "net": "", "language": "-1", "from": "web", "race": "", "iframe": "1", "url2": "https://mail.163.com/errorpage/error163.htm", "product": "mail163" } cookies = 'NTES_SESS=' + self.session.cookies.get_dict( )['NTES_SESS'] self.session.headers.update({'Cookie': cookies}) resp = self.session.post( 'https://mail.163.com/entry/cgi/ntesdoor?', data=form_data, allow_redirects=False) redirect_url = resp.headers['location'] if 'sid' in redirect_url: self.logger.info('登录成功! ') response = self.session.get(redirect_url) # 网易邮箱的关键是这个sid, 是session id 的意思, 有了它就可以爬邮箱了, 当然还要有配套的 Cookie cookies_item = { 'cookies': resp.cookies.get_dict(), 'sid': response.cookies.get_dict()['Coremail.sid'] } self.redis_client.save_cookies(self.site, self.username, cookies_item) try: nickname = re.search("'true_name':'(.*?)'", response.text).group(1) self.logger.info('Hello, {}! '.format(nickname)) except: self.logger.info('你还没有设置昵称, 快去设置...') return True raise Exception('登录失败! ') elif res['ret'] == '413': self.reset_flag = True raise Exception('账号或密码错误! ') elif res['ret'] == '445': self.logger.warning('验证码校验...') return False elif res['ret'] == '409': self.logger.warning('登录过于频繁, 请稍后再试! ') return False elif res['ret'] == '423': self.logger.warning('风控账号! ') return False raise Exception('登录失败! ') @check_user() def run(self, load_cookies: bool = True): if '163.com' not in self.username: self.username += '@163.com' if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): read_flag = input('是否需要查看邮箱? (yes/任意键退出) >> \n') if read_flag == 'yes': self._read_email(cookies) return True self.logger.warning('Cookies 已过期') self.login()
class LiepinLogin: def __init__(self, username: str = None, password: str = None): self.site = 'liepin' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Referer': 'https://www.liepin.com/', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36', 'DNT': '1', } # 密码错误重置初始化 self.reset_flag = False self.timestamp = str(int(time.time() * 1000)) def check_islogin(self, cookies): url = 'https://c.liepin.com/main/page.json?' res = self.session.get(url, cookies=cookies, allow_redirects=False) try: result = json.loads(res.text) if result['status'] == 0 and result['message'] == 'OK': self.logger.info('登录成功!') self.logger.info('Hello, {}! '.format( result['data']['userCForm']['c_name'])) self.redis_client.save_cookies(self.site, self.username, cookies) return True except: return False @staticmethod def _loads_jsonp(_jsonp): """ 解析jsonp数据格式为json :return: """ try: return json.loads(re.match(".*?({.*}).*", _jsonp, re.S).group(1)) except ValueError: raise ValueError('Invalid Input') def _get_token(self): """ 获取用户token和加密js :return: """ params = { 'sign': self.username, 'callback': 'jQuery171029989774566236793_' + self.timestamp, '_': self.timestamp, } response = self.session.get( 'https://passport.liepin.com/verificationcode/v1/js.json', params=params) return self._loads_jsonp(response.text) def _encrypt_pwd(self): md5 = hashlib.md5() md5.update(self.password.encode('utf-8')) return md5.hexdigest() @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 模拟登录 :return: """ result = self._get_token() token = result.get('data').get('token') js = result.get('data').get('js') ctx = execjs.compile(js) value = ctx.call('encryptData', self.username) pwd = self._encrypt_pwd() params = { 'callback': 'jQuery17108618602708711502_' + self.timestamp, 'login': self.username, 'pwd': pwd, 'token': token, 'value': value, 'url': '', '_bi_source': '0', '_bi_role': '0', '_': self.timestamp, } res = self.session.get( 'https://passport.liepin.com/account/individual/v1/login.json', params=params) cookies = res.cookies.get_dict() result = json.loads( re.search(f'{params["callback"]}\((.*)\)', res.text).group(1)) if self.check_islogin(cookies): return True if '密码错误' in res.text: self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: {}'.format(result['msg'])) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期') self.login()
class MeituanLogin: def __init__(self, username: str = None, password: str = None): self.site = 'meituan' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Referer': 'https://epassport.meituan.com/account/unitivelogin?bg_source=3&service=waimai&platform=2&continue=http://e.waimai.meituan.com/v2/epassport/entry&left_bottom_link=%2Faccount%2Funitivesignup%3Fbg_source%3D3%26service%3Dwaimai%26platform%3D2%26continue%3Dhttp%3A%2F%2Fe.waimai.meituan.com%2Fv2%2Fepassport%2FsignUp%26extChannel%3Dwaimaie%26ext_sign_up_channel%3Dwaimaie&right_bottom_link=%2Faccount%2Funitiverecover%3Fbg_source%3D3%26service%3Dwaimai%26platform%3D2%26continue%3Dhttp%3A%2F%2Fe.waimai.meituan.com%2Fv2%2Fepassport%2FchangePwd', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' } with open('./js/encrypt_pwd.js', 'rb') as f: self.pwd_js = f.read().decode() with open('./js/slider.js', 'rb') as f: self.slider_js = f.read().decode() with open('./js/token.js', 'rb') as f: self.token_js = f.read().decode() # 密码错误重置初始化 self.reset_flag = False @staticmethod def check_islogin(cookies): """ 检查是否登录成功 :return: """ url = 'https://www.meituan.com/ptapi/getLoginedUserInfo' result = requests.get( url, cookies=cookies, headers={ 'Referer': 'https://www.meituan.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' }).json() if 'nickName' in set(result.keys()): return True return False @staticmethod def _get_fingerprint(): # 进入浏览器设置 options = Options() # 设置中文 options.add_argument('lang=zh_CN.UTF-8') options.add_argument('--headless') options.add_argument( 'user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"' ) browser = webdriver.Chrome(options=options) browser.get('file:///D:/Meituan/fingerprint.html') time.sleep(2) html = browser.page_source fingerprint = re.search('</script>(.*?)</body>', html).group(1) browser.quit() return fingerprint def _get_token(self, url): ctx = execjs.compile(self.token_js) return ctx.call('get_token', url) def _get_behavior_token(self, page_data): ctx = execjs.compile(self.slider_js) verify_data = ctx.call('get_behavior_token', page_data) return verify_data def _encrypt_pwd(self): """ RSA加密密码 :return: """ ctx = execjs.compile(self.pwd_js) pwd = ctx.call('encrypt', self.password) return pwd def _init_slider(self, requests_code): """ 初始化滑块 :param requests_code: :return: """ data = { 'requestCode': requests_code, 'feVersion': '1.4.0', 'source': '1' } url = 'https://verify.meituan.com/v2/ext_api/page_data' result = self.session.post(url, data=data).json() if result['status']: return result['data'] return None def _slider_verify(self, code, request_code): """ 滑块风控验证 :param code: :param request_code: :return: """ page_data = None for _ in range(5): page_data = self._init_slider(request_code) if page_data: break if not page_data: raise Exception('滑块初始化失败! ') url = 'https://verify.meituan.com/v2/ext_api/merchantlogin/verify?id=71' verify_data = self._get_behavior_token(page_data) self.session.headers.update({ 'Authorization': 'Bearer ' + request_code, 'Content-Type': 'application/x-www-form-urlencoded', 'Origin': 'https://epassport.meituan.com', }) data = { 'request_code': request_code, 'behavior': verify_data['behavior'], 'fingerprint': '', '_token': verify_data['token'], } result = self.session.post(url, data=data).json() if result['status']: self.logger.info('成功通过滑块验证!') if code == 121060: return code elif code == 101190: return result['data']['response_code'] else: raise Exception('未知类型请求! ') raise Exception('滑块验证失败! ') def _init_captcha(self, request_code): url = 'https://verify.meituan.com/v2/captcha?' params = { 'request_code': request_code, 'action': 'login', 'randomId': '0.6868395303586443', '_token': self._get_token(url) } resp = self.session.get(url, params=params) with open('captcha.png', 'wb') as f: f.write(resp.content) img = Image.open('captcha.png') img.show() verify_code = input('验证码 >> \n') return verify_code def _captcha_verify(self, code, verify_code): """ 图文识别验证码 :param code: :param verify_code: :return: """ url = 'https://verify.meituan.com/v2/ext_api/login/verify?id=1' data = { 'id': '71', 'request_code': code, 'captchacode': verify_code, '_token': self._get_token(url) } resp = self.session.post(url, data=data).json() if resp['status'] == 1: return resp['data']['response_code'] return None def _init_smscode(self, request_code): """ 请求手机验证码接口 :return: """ url = 'https://verify.meituan.com/v2/ext_api/loginverification/info?id=4' data = { 'request_code': request_code, 'mobile': '', 'moduleEnable': 'true', 'listIndex': 0, '_token': self._get_token(url) } resp = self.session.post(url, data=data).json() if resp['status'] == 0: msg = resp['error']['message'] self.logger.warning(msg) code = resp['error']['code'] request_code = resp['error']['request_code'] success = self._slider_verify(code, request_code) if success: return True return False elif resp['status'] == 1: return True return False def _smscode_verify(self, request_code): """ 发送手机验证码 :return: """ success = False for _ in range(5): success = self._init_smscode(request_code) if success: break if not success: raise Exception('手机验证码接口请求失败! ') url = 'https://verify.meituan.com/v2/ext_api/loginverification/verify?id=4' time.sleep(30) sms_code = input('请输入手机验证码 >> \n') data = { 'mobile': '', 'request_code': request_code, 'smscode': sms_code, 'listIndex': 0, '_token': self._get_token(url) } resp = self.session.post(url, data=data).json() if resp['status'] == 1: self.logger.info('成功通过手机验证! ') return resp['data']['response_code'] raise Exception('手机验证失败! ') def _get_csrf(self): """ 获取认证Csrf参数 :return: """ url = 'https://passport.meituan.com/account/unitivelogin?service=www&continue=https%3A%2F%2Fwww.meituan.com%2Faccount%2Fsettoken%3Fcontinue%3Dhttp%253A%252F%252Fcd.meituan.com%252F' resp = self.session.get(url) soup = BeautifulSoup(resp.text, 'lxml') csrf = soup.select('input[name="csrf"]')[0]['value'] return csrf def login(self, csrf, request_code='', response_code=''): """ 模拟登录 :return: """ login_api = 'https://passport.meituan.com/account/unitivelogin?risk_partner=0&risk_platform=1&risk_app=-1&uuid=ea8b149299ce4622b486.1568870998.1.0.0&service=www&continue=https%3A%2F%2Fwww.meituan.com%2Faccount%2Fsettoken%3Fcontinue%3Dhttps%253A%252F%252Fhf.meituan.com%252F' pwd = self._encrypt_pwd() self.session.headers.update({ 'Referer': 'https://passport.meituan.com/account/unitivelogin?service=www&continue=https%3A%2F%2Fwww.meituan.com%2Faccount%2Fsettoken%3Fcontinue%3Dhttp%253A%252F%252Fcd.meituan.com%252F', 'X-CSRF-Token': csrf, 'X-Client': 'javascript', 'X-Requested-With': 'XMLHttpRequest', }) if 'Authorization' in set(self.session.headers): del self.session.headers['Authorization'] data = { 'countrycode': '86', 'email': self.username, 'password': pwd, 'origin': 'account-login', 'csrf': csrf, 'requestCode': request_code, 'responseCode': response_code, 'h5Fingerprint': '' } result = self.session.post(login_api, data=data).json() return result @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login_process(self): """ 整体登录流程 :return: """ csrf = self._get_csrf() result = self.login(csrf) if 'data' in set(result.keys()): self.logger.info('登录成功! ') # token = result['data']['token'] nickname = result['data']['userName'] self.logger.info('Hello, {}! '.format(nickname)) cookies = self.session.cookies.get_dict() if self.check_islogin(cookies): self.logger.info('Cookies 有效! ') self.redis_client.save_cookies(self.site, self.username, cookies) return cookies else: raise Exception('登录失败! ') else: if result['error']['code'] == 101190: msg = result['error']['message'] self.logger.warning(msg) code = result['error']['code'] request_code = result['error']['data']['requestCode'] response_code = self._slider_verify(code, request_code) result = self.login(csrf, request_code, response_code) if result['success']: self.logger.info('登录成功! ') nickname = result['data']['userName'] self.logger.info('Hello, {}! '.format(nickname)) cookies = self.session.cookies.get_dict() if self.check_islogin(cookies): self.logger.info('Cookies 有效! ') self.redis_client.save_cookies(self.site, self.username, cookies) return cookies else: raise Exception('登录失败! ') else: if result['error']['code'] == 101157: msg = result['error']['message'].replace('验证', '手机验证') self.logger.warning(msg) request_code = result['error']['data']['param'].split( '&')[1].split('=')[1] response_code = self._smscode_verify(request_code) result = self.login(request_code, response_code) if result.get('data', 0): self.logger.info('登录成功! ') nickname = result['data']['userName'] self.logger.info('Hello, {}! '.format(nickname)) cookies = self.session.cookies.get_dict() if self.check_islogin(cookies): self.logger.info('Cookies 有效! ') self.redis_client.save_cookies( self.site, self.username, cookies) return cookies else: raise Exception('Cookies 失效! ') else: raise Exception('登录失败! ') elif result['error']['code'] == 101135: msg = result['error']['message'] self.logger.warning(msg) return None else: raise Exception('登录失败: ', result['error']['message']) elif result['error']['code'] == 101157: msg = result['error']['message'].replace('验证', '手机验证') self.logger.warning(msg) request_code = result['error']['data']['param'].split( '&')[1].split('=')[1] response_code = self._smscode_verify(request_code) result = self.login(request_code, response_code) if result.get('data', 0): self.logger.info('登录成功! ') nickname = result['data']['userName'] self.logger.info('Hello, {}! '.format(nickname)) cookies = self.session.cookies.get_dict() if self.check_islogin(cookies): self.logger.info('Cookies 有效! ') self.redis_client.save_cookies(self.site, self.username, cookies) return cookies else: raise Exception('Cookies 失效! ') else: raise Exception('登录失败! ') elif result['error']['code'] == 101135: msg = result['error']['message'] self.logger.warning(msg) return None raise Exception('登录失败: ', result['error']['message']) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login_process()
class GithubLogin: def __init__(self, username: str = None, password: str = None): self.site = 'github' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): res = requests.get('https://github.com/', cookies=cookies) if 'octolytics-actor-login' in res.text: self.logger.info('Cookies 有效! ') bsobj = BeautifulSoup(res.text, 'lxml') nickname = bsobj.find( 'meta', {'name': 'octolytics-actor-login'})['content'] self.logger.info('Hello, {}! '.format(nickname)) return True return False def _get_authenticity_token(self): """ 请求登录页获取 authenticity_token :return: """ res = self.session.get('https://github.com/login') bsobj = BeautifulSoup(res.text, 'lxml') authenticity_token = bsobj.find( 'input', {'name': 'authenticity_token'})['value'] return authenticity_token @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 模拟登录 :return: """ login_api = 'https://github.com/session' authenticity_token = self._get_authenticity_token() data = { 'commit': 'Sign in', 'utf8': '✓', 'authenticity_token': authenticity_token, 'login': self.username, 'password': self.password, 'webauthn-support': 'supported' } res = self.session.post(login_api, data=data, allow_redirects=False) if res.status_code == 302: self.logger.info('登录成功! ') self.redis_client.save_cookies(self.site, self.username, res.cookies.get_dict()) return True elif 'Incorrect username or password' in res.text: self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败! ') @check_user() def run(self, load_cookies: bool = True): """ 主函数运行 :return: """ if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期! ') self.login()
class WeiboLogin: def __init__(self, username: str = None, password: str = None): self.site = 'weibo' self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.username = username self.password = password self.session = requests.session() self.session.headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Referer': 'https://weibo.com/', 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Mobile Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): """ 检查是否登录成功 :return: """ res = self.session.get('http://my.sina.com.cn/', cookies=cookies) html = res.content.decode(chardet.detect(res.content)['encoding']) bsobj = BeautifulSoup(html, 'lxml') if bsobj.find('p', {'class': 'me_name'}): nickname = bsobj.find('p', {'class': 'me_name'}).get_text() self.logger.info('Cookies 有效! ') self.logger.info(f'Hello, {nickname}! ') self.redis_client.save_cookies(self.site, self.username, cookies) return True elif '立即登录' in res.text: return False return False @staticmethod def _load_js(jsfilename): """ 打开js脚本并加载 :return: """ with open(jsfilename, 'r') as f: js = f.read() ctx = execjs.compile(js) return ctx def _get_su(self, ctx): """ 获取su参数 :return: """ su = ctx.call('getSu', self.username) return su def _get_params(self, su): """ 获取pcid, nonce, pubkey, servertime, rsakv :param su: :return: """ url = 'https://login.sina.com.cn/sso/prelogin.php?' params = { 'entry': 'weibo', 'callback': 'sinaSSOController.preloginCallBack', 'su': su, 'rsakt': 'mod', 'checkpin': '1', 'client': 'ssologin.js(v1.4.19)', '_': int(time.time() * 1000) } res = self.session.get(url, params=params).text result = json.loads( re.search(r'sinaSSOController.preloginCallBack\((.*?)\)', res).group(1)) pcid = result['pcid'] nonce = result['nonce'] pubkey = result['pubkey'] servertime = result['servertime'] rsakv = result['rsakv'] return pcid, nonce, pubkey, servertime, rsakv def _get_verifycode(self, pcid): captcha_url = f'https://login.sina.com.cn/cgi/pin.php?r=16343619&s=0&p={pcid}' img_data = self.session.get(captcha_url).content self.logger.info('使用超级鹰识别验证码...') ok, result = image_to_text(img_data) if ok: self.logger.info('成功识别验证码!') return result raise Exception('验证码识别失败: ', result) def _get_sp(self, ctx, pubkey, servertime, nonce): """ 获取sp参数 :param ctx: :return: """ sp = ctx.call('getSp', pubkey, servertime, nonce, self.password) return sp @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 登录 :return: """ login_api = f'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.19)&_={int(time.time()*1000)}' ctx = self._load_js('encryptUN.js') su = self._get_su(ctx) pcid, nonce, pubkey, servertime, rsakv = self._get_params(su) verify_code = self._get_verifycode(pcid) ctx_ = self._load_js('encryptPW.js') sp = self._get_sp(ctx_, pubkey, servertime, nonce) data = { 'entry': 'weibo', 'gateway': '1', 'from': '', 'savestate': '7', 'qrcode_flag': 'false', 'useticket': '1', 'pagerefer': 'https://login.sina.com.cn/crossdomain2.php?action=logout&r=https%3A%2F%2Fpassport.weibo.com%2Fwbsso%2Flogout%3Fr%3Dhttps%253A%252F%252Fweibo.com%26returntype%3D1', 'pcid': pcid, 'door': verify_code, 'vsnf': '1', 'su': su, 'service': 'miniblog', 'servertime': servertime, 'nonce': nonce, 'pwencode': 'rsa2', 'rsakv': rsakv, 'sp': sp, 'sr': '1440*561', # 屏幕大小 'encoding': 'UTF-8', 'cdult': '2', 'domain': 'weibo.com', 'prelt': '48', 'returntype': 'TEXT' } res = self.session.post(login_api, data=data) cookies = res.cookies.get_dict() if self.check_islogin(cookies): return True elif res.json()['reason'] == '登录名或密码错误' or res.json( )['reason'] == '请输入正确的密码': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception(res.json()['reason']) @check_user() def run(self, load_cookies: bool = True): """ 主函数运行 :return: """ if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期! ') self.login()
class YYlogin: def __init__(self, username: str = None, password: str = None): self.site = 'yy' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Referer': 'http://www.yy.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0' } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): url = 'http://www.yy.com/yyweb/user/queryUserInfo.json??callback=jQuery1111045940186663574223_{}'.format(int(time.time() * 1000)) res = self.session.get(url, cookies=cookies).json() if res['resultCode'] == 0: self.logger.info('Cookies 有效! ') self.logger.info('Hello, {}! '.format(res['data']['nick'])) return True return False def _init_cookies(self): self.session.get('http://www.yy.com/') def _get_url(self): while True: url = 'http://www.yy.com/login/getSdkAuth?embed=true&cssid=5719_1' res = self.session.post(url).json() if res['success'] == 1: ttokensec = res['ttokensec'] token_url = res['url'] return ttokensec, token_url time.sleep(random.random()) def _get_token(self, url): res = self.session.get(url) oauth_token = re.search('oauth_token: "(.*?)"', res.text, re.S).group(1) return oauth_token def _encrypt_pwd(self): with open('encrypt.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) return ctx.call('encryptPwd', self.password) def _get_captcha(self, captcha_id): base_url = 'https://captcha.yy.com/pickwords/init.do?' params = { 'appid': 5719, 'random': int(time.time() * 1000), 'busiId': 'busiid', 'captchaId': captcha_id, 'callback': f'JSONP_{int(time.time() * 1000)}' } res = self.session.get(base_url, params=params) with open('captcha.png', 'wb') as f: f.write(res.content) # img = Image.open('captcha.png') # img.show() @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): login_api = 'https://lgn.yy.com/lgn/oauth/x2/s/login_asyn.do' self._init_cookies() ttokensec, token_url = self._get_url() oauth_token = self._get_token(token_url) pwd = self._encrypt_pwd() data = { 'username': self.username, 'pwdencrypt': pwd, 'oauth_token': oauth_token, 'denyCallbackURL': 'http://www.yy.com/login/udbCallback?cancel=1', 'UIStyle': 'xelogin', 'appid': 5719, 'cssid': 5719_1, 'mxc': '', 'vk': '', 'isRemMe': '1', 'mmc': '', 'vv': '', 'hiido': '1' } res = requests.post(login_api, data=data).json() if res['code'] == '0': callback_url = res['obj']['callbackURL'] r_cookies = {"_pwcWyy":"066696ef76ca2f85","cookieDate":"1564841513462","hd_newui":"0.970057832001334","hdjs_session_id":"0.5300241632973548","hdjs_session_time":"1564842336414","hiido_ui":"0.7355867355495954","Hm_lpvt_c493393610cdccbddc1f124d567e36ab":"1564842337","Hm_lvt_c493393610cdccbddc1f124d567e36ab":"1564841491,1564841511,1564842337","udboauthtmptoken":"undefined","udboauthtmptokensec":ttokensec} resp = self.session.get(callback_url, cookies=r_cookies) if 'loginSuccess' in resp.text: final_url = re.search(r"writeCrossmainCookieWithCallBack\('(.*?)',", resp.text).group(1) response = self.session.get(final_url) if 'write cookie for oauth' in response.text: self.logger.info('登录成功!') cookies = response.cookies.get_dict() self.redis_client.save_cookies(self.site, self.username, cookies) return True raise Exception('登录失败! ') elif res['code'] == '1000010': self.reset_flag = True raise Exception('账号或密码错误! ') elif res['code'] == '1000001': self.logger.warning(res['msg']) captcha_id = re.search('captchaId: "(.*?)",', res['obj']['itvjs'], re.S).group(1) self._get_captcha(captcha_id) elif res['code'] == '1000003': self.logger.warning(res['msg']) return False raise Exception('登录失败! ') @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期') self.login()
class MiguLogin: def __init__(self, username: str = None, password: str = None): self.site = 'migu' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Referer': 'https://passport.migu.cn/login?sourceid=220001&apptype=0&forceAuthn=false&isPassive=false&authType=MiguPassport&passwordControl=0&display=web&referer=http://music.migu.cn/v3&logintype=1&qq=null&weibo=null&alipay=null&weixin=null&phoneNumber=&callbackURL=http%3A%2F%2Fmusic.migu.cn%2Fv3&relayState=', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest' } # 密码错误重置初始化 self.reset_flag = False self.ctx = self._load_js() def check_islogin(self, cookies): res = self.session.get('http://music.migu.cn/v3/my', cookies=cookies) html = res.content.decode(chardet.detect(res.content)['encoding']) if '我的收藏' in html: bsobj = BeautifulSoup(html, 'lxml') nickname = bsobj.find('h4', { 'class': 'nickname' }).get_text().strip() self.logger.info('登录成功!') self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False def _load_js(self): """ 加载编译js文件 :return: """ with open('encrypt.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) return ctx def encrypt_pwd(self): """ RSA加密密码 :return: """ encrypt_pwd = self.ctx.call('encryptPwd', self.password) # print('加密密码', encrypt_pwd) return encrypt_pwd def _get_fingerprint(self): """ 获取浏览器指纹 :return: """ finger_print = self.ctx.call('getFingerprint') # print('浏览器指纹', finger_print) return finger_print @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def _get_token(self): """ 模拟登录 :return: """ login_api = 'https://passport.migu.cn/authn' data = { 'sourceID': '220001', 'appType': '0', 'relayState': '', 'loginID': self.username, 'enpassword': self.encrypt_pwd(), 'captcha': '', 'imgcodeType': '1', 'rememberMeBox': '1', 'fingerPrint': self._get_fingerprint()['result'], 'fingerPrintDetail': self._get_fingerprint()['details'], 'isAsync': 'true' } res = self.session.post(login_api, data=data).json() if res['status'] == 2000: token = res['result']['token'] return token elif res['message'] == '密码验证未通过' or res['message'] == '帐号或密码错误': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: ', res['message']) def login(self): """ 登录认证 :return: """ token = self._get_token() if token: params = {'callbackURL': '', 'relayState': '', 'token': token} res = self.session.get('http://music.migu.cn/v3/user/login?', params=params) cookies = res.cookies.get_dict() if self.check_islogin(cookies): return True return False @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期') self.login()
class QimaiLogin: def __init__(self, username: str = None, password: str = None): self.site = 'qimai' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Origin': 'https://www.qimai.cn', 'Referer': 'https://www.qimai.cn/account/signin/r/%2Frank%2Findex%2Fbrand%2Ffree%2Fcountry%2Fcn%2Fgenre%2F5000%2Fdevice%2Fiphone', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): url = 'https://api.qimai.cn/account/settingAccount?analysis=eEcbVwJTX0VeRB9DXRBDUQpTdwJTX0VeRHATFVUCCVEFBFQEAAgGAgFwG1U%3D' res = self.session.get(url, cookies=cookies).json() if res['code'] == 10000 and res['msg'] == '成功': self.logger.info('登录成功!') self.logger.info('Hello, {}! '.format( res['accountInfo']['realname'])) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def _get_verifycode(self): timestamp = int(time.time() * 1000) captcha_url = f'https://api.qimai.cn/account/getVerifyCodeImage?{timestamp}' img_data = self.session.get(captcha_url).content self.logger.info('使用超级鹰识别验证码...') ok, result = image_to_text(img_data) if ok: self.logger.info('成功识别验证码!') return result raise Exception('验证码识别失败: ', result) def _get_synct(self): resp = self.session.get('https://www.qimai.cn/rank') cookies = resp.cookies.get_dict() return cookies.get('synct') @staticmethod def _get_analysis(synct): with open('analysis.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) return ctx.call('getLoginAnalysis', synct) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self, login_api): data = { 'username': self.username, 'password': self.password, 'code': self._get_verifycode() } res = self.session.post(login_api, data=data) cookies = res.cookies.get_dict() if self.check_islogin(cookies): return cookies elif res.json()['msg'] == '用户名或密码错误': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: ', res.json()['msg']) @check_user() def run(self, load_cookies: bool = True): """ 模拟登录 :param load_cookies: :return: """ synct = self._get_synct() url = 'https://api.qimai.cn/account/signinForm?' params = {'analysis': self._get_analysis(synct)} login_api = url + urlencode(params) if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login(login_api)
class QichamaoLogin: def __init__(self, username: str = None, password: str = None): self.site = 'qichamao' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置 self.reset_flag = True @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def _get_verifycode(self): captcha_url = 'https://www.qichamao.com/usercenter/varifyimage?' img_data = self.session.get(captcha_url).content self.logger.info('使用超级鹰识别验证码...') ok, result = image_to_text(img_data) if ok: self.logger.info('成功识别验证码!') return result raise Exception('验证码识别失败: ', result) def check_islogin(self, cookies): """ 检查登录状态,访问登录页面跳转则是已登录, 如登录成功保存当前 Cookies :return: bool """ login_url = 'https://www.qichamao.com/usercenter/login' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299' } resp = requests.get(login_url, cookies=cookies, headers=headers) if 'userhd_name' in resp.text: self.logger.info('登录成功! ') bsobj = BeautifulSoup(resp.text, 'lxml') nickname = bsobj.select('.userhd_name')[0].get_text() self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): login_api = 'https://www.qichamao.com/usercenter/dologin' verify_code = self._get_verifycode() data = { 'userId': self.username, 'password': self.password, 'VerifyCode': verify_code, 'sevenDays': 'false' } res = self.session.post(login_api, data=data) cookies = res.cookies.get_dict() if self.check_islogin(cookies): return True elif '用户名或密码错误' in res.json()['sMsg']: raise Exception('账号或密码错误! ') raise Exception(json.loads(res.text)['sMsg']) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期') self.login()
class TaobaoLogin: def __init__(self, username: str = None, password: str = None): self.site = 'taobao' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) async def check_islogin(self, page): """ 检查登录状态: 访问首页, 出现用户名则登录成功 :param page: :return: """ await page.goto('https://www.taobao.com/') time.sleep(3) html = await page.content() bsobj = BeautifulSoup(html, 'lxml') if bsobj.find('a', {'class': 'site-nav-login-info-nick'}): self.logger.info('Cookies 有效! ') nickname = bsobj.find('a', {'class': 'site-nav-login-info-nick'}).get_text() self.logger.info('Hello, {}! '.format(nickname)) cookies = await page.cookies() # cookies = {item['name']: item['value'] for item in cookies} self.redis_client.save_cookies(self.site, self.username, cookies) return True return False @staticmethod def input_time_random(): return random.randint(150, 201) def retry_if_result_none(self, result): self.logger.warning('滑块判定失败, 重试!') return result is None @check_user() async def login(self): # 以下使用await 可以针对耗时的操作进行挂起 # 记:一定要给pyppeteer权限删除用户数据, 即设置userDataDir: 文件夹名称, 否则会报错无法移除用户数据 browser = await launch( { 'headless': True, 'args': ['--no-sandbox', '--disable-infobars'], }, # userDataDir=r'D:\login\userdata', args=['--window-size=1366, 768'] ) page = await browser.newPage() # 启动个新的浏览器页面 await page.setJavaScriptEnabled(enabled=True) # 启用js await page.setUserAgent( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299' ) # 设置模拟浏览器 self.logger.info('尝试登录...') await page.goto( 'https://login.taobao.com/member/login.jhtml?spm=a21bo.2017.754894437.1.5af911d9qqVAb1&f=top&redirectURL=https%3A%2F%2Fwww.taobao.com%2F' ) await self.page_evaluate(page) # 修改window.navigator.webdriver = False, 这是绕过淘宝自动化工具检测的关键 # 使用type选定页面元素,并修改其数值,用于输入账号密码,修改的速度仿人类操作,因为有个输入速度的检测机制 # 因为 pyppeteer 框架需要转换为js操作,而js和python的类型定义不同,所以写法与参数要用字典,类型导入 try: await page.click('#J_Quick2Static') # 点击选择密码登录, 看你进入登录页面是否已经是密码登录, 若不是则需要点击选择密码登录 except: pass time.sleep(1) # 清空用户名输入框, 确保正确输入: 用户名不为空时页面才会有清空节点 nickx try: await page.click('.nickx') except: pass # await page.evaluate('''() => { document.getElementById(TPL_username_1).value="" }''') time.sleep(1) await page.type('#TPL_username_1', self.username, {'delay': self.input_time_random() - 50}) time.sleep(1) await page.type('#TPL_password_1', self.password, {'delay': self.input_time_random()}) # await page.screenshot({'path': './headless-test-result.png'}) # 截图测试 time.sleep(2) slider = await page.Jeval('#nocaptcha', 'node => node.style') # 是否有滑块 self.logger.info('账号密码输入完成, 判断滑块是否出现') if slider: self.logger.info('出现滑块情况判定') # await page.screenshot({'path': './headless-login-slide.png'}) # 截图测试 flag = await self.mouse_slide(page=page) # js拉动滑块 if flag: # await page.keyboard.press('Enter') # 模拟按键Enter确定 或鼠标点击登录 await page.click('#J_SubmitStatic') time.sleep(1) cookies = await page.cookies() # cookies = {item['name']: item['value'] for item in cookies} self.redis_client.save_cookies(self.site, self.username, cookies) else: # await page.keyboard.press('Enter') self.logger.info('滑块未出现, 点击登录按钮') await page.click('#J_SubmitStatic') await page.waitFor(20) await page.waitForNavigation() # 等待跳转 try: global error error = await page.Jeval('.error', 'node => node.textContent') # 检测是否是账号密码错误 except Exception as e: error = None self.logger.info("登录成功! ") finally: if error: # self.logger.info('确保账户安全重新输入') error = await (await (await page.xpath('//div[@class="dialog-con"]'))[0].getProperty( 'textContent')).jsonValue() self.logger.info(error) else: await asyncio.sleep(3) cookies = await page.cookies() # print({item['name']: item['value'] for item in cookies}) self.redis_client.save_cookies(self.site, self.username, cookies) return cookies await self.page_close(browser) async def page_evaluate(self, page): # 替换淘宝在检测浏览时采集的一些参数。 # 就是在浏览器运行的时候,始终让window.navigator.webdriver=false # navigator是windiw对象的一个属性,同时修改plugins,languages,navigator await page.evaluate( '''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => undefined } }) }''') # 以下为插入中间js,将淘宝会为了检测浏览器而调用的js修改其结果。 await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {}, }; }''') await page.evaluate( '''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''') await page.evaluate( '''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''') async def page_close(self, browser): """ 关闭浏览器驱动 :param browser: :return: """ for _page in await browser.pages(): await _page.close() await browser.close() @retry(retry_on_result=retry_if_result_none, ) async def mouse_slide(self, page=None): await asyncio.sleep(2) try: # 鼠标移动到滑块,按下,滑动到头(然后延时处理),松开按键 await page.hover('#nc_1_n1z') # 不同场景的验证码模块可能名字不同。 await page.mouse.down() # 模拟按下鼠标 await page.mouse.move(2000, 0, {'delay': random.randint(1000, 2000)}) # js模拟拖动 await page.mouse.up() # 模拟松开鼠标 except Exception as e: self.logger.error('验证失败: {}'.format(e.args)) return None, page else: await asyncio.sleep(2) slider_again = await page.Jeval('.nc-lang-cnt', 'node => node.textContent') # 判断是否通过 if slider_again != '验证通过': return None, page else: # await page.screenshot({'path': './headless-slide-result.png'}) # 截图测试 self.logger.info('验证通过! ') return 1, page async def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: browser = await launch( { 'headless': True, 'args': ['--no-sandbox', '--disable-infobars'], }, userDataDir=r'D:\login\userdata', args=['--window-size=1366, 768'] ) page = await browser.newPage() self.logger.info('将 cookies 装载到浏览器中...') await page.deleteCookie() for cookie in cookies: await page.setCookie(cookie) if await self.check_islogin(page): await self.page_close(browser) return cookies await self.page_close(browser) self.logger.warning('cookies 已过期! ') return await self.login()
class QixinbaoLogin: def __init__(self, username: str = None, password: str = None): self.site = 'qixinbao' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Content-Type': 'application/json;charset=UTF-8', # payload提交表单参数, 请求头中必须含有 Content-Type, 并且提交方法为 json.dumps(data) 'Referer': 'https://www.qixin.com/auth/login?return_url=%2F', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest' } # 密码错误重置 self.reset_flag = False with open('encrypt.js', 'rb') as f: js = f.read().decode() self.ctx = execjs.compile(js) self.codes = self.get_codes() @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def check_islogin(self, cookies): url = 'https://www.qixin.com/user/home/center' res = self.session.get(url, cookies=cookies) if '会员中心' in res.text: bsobj = BeautifulSoup(res.text, 'lxml') nickname = bsobj.find('div', {'class': 'body'}).find('h5').get_text() self.logger.info('登录成功!') self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False def _encrypt_pwd(self): """ 加密密码 :return: """ return self.ctx.call('encrypt', self.password) @staticmethod def get_codes(): """ 获取 js 加密 codes :return: """ headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36', } url = 'https://cache.qixin.com/pcweb/common.a89140e8.js' resp = requests.get(url, headers=headers) codes = {} for i in range(20): text = r'e\.default={' + str(i) + ':"(.*?)"}' x = re.search(text, resp.text).group(1) codes.setdefault(i, x) return codes def _get_header_js(self, url, data): """ 获取请求头中的加密参数, 加密对象为提交的表单 :return: """ return {self.ctx.call('header_key', self.codes, url): self.ctx.call('header_value', self.codes, url, data)} def _init_cookies(self): self.session.get('https://www.qixin.com/') @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 模拟登录 :return: """ self._init_cookies() login_api = 'https://www.qixin.com/api/user/login' pwd = self._encrypt_pwd() data = { 'acc': self.username, 'captcha': { 'isTrusted': 'true' }, 'keepLogin': '******', 'pass': pwd } header_js = self._get_header_js(login_api, data) self.session.headers.update(header_js) res = self.session.post(login_api, data=json.dumps(data)) cookies = self.session.cookies.get_dict() if self.check_islogin(cookies): return cookies elif '用户名或密码错误' in res.text: self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: ', res.json()['message']) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login()
class ZhilianLogin: def __init__(self, username: str = None, password: str = None): self.site = 'zhilian' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.browser = None self.wait = None def check_islogin(self, cookies): """ 检查登录状态, 跳转至个人主页则 cookies 有效 :param cookies: :return: """ url = 'https://fe-api.zhaopin.com/c/i/user/detail' headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } res = requests.get(url, headers=headers, cookies=cookies).json() if res['code'] == 200: self.logger.info('Cookies 有效!') nickname = res['data']['Name'] self.logger.info('Hello, {}! '.format(nickname)) return True return False def get_geetest_button(self): button = self.wait.until( EC.element_to_be_clickable( (By.CLASS_NAME, 'geetest_slider_button'))) return button def get_geetest_image(self, name, full): top, bottom, left, right, size = self.get_position(full) # print("验证码位置", top, bottom, left, right) screenshot = self.get_screenshot() captcha = screenshot.crop((left, top, right, bottom)) size = size["width"] - 1, size["height"] - 1 captcha.thumbnail(size) # captcha.show() # captcha.save(name) return captcha def get_position(self, full): img = self.wait.until( EC.presence_of_element_located( (By.CSS_SELECTOR, "canvas.geetest_canvas_slice"))) fullbg = self.wait.until( EC.presence_of_element_located( (By.CSS_SELECTOR, "canvas.geetest_canvas_fullbg"))) time.sleep(2) # 两种执行js写法 if full: self.browser.execute_script( 'document.getElementsByClassName("geetest_canvas_fullbg")[0].setAttribute("style", "")' ) else: self.browser.execute_script( "arguments[0].setAttribute(arguments[1], arguments[2])", fullbg, "style", "display: none") location = img.location size = img.size top, bottom, left, right = location["y"], location["y"] + \ size["height"], location["x"], location["x"] + size["width"] return (top, bottom, left, right, size) def get_screenshot(self): screenshot = self.browser.get_screenshot_as_png() return Image.open(BytesIO(screenshot)) def get_gap(self, image1, image2): for i in range(LEFT, image1.size[0]): for j in range(image1.size[1]): if not self.is_pixel_equal(image1, image2, i, j): return i return LEFT def is_pixel_equal(self, image1, image2, x, y): pixel1 = image1.load()[x, y] pixel2 = image2.load()[x, y] if abs(pixel1[0] - pixel2[0]) < THRESHOLD and abs( pixel1[1] - pixel2[1]) < THRESHOLD and abs( pixel1[2] - pixel2[2]) < THRESHOLD: return True else: return False def get_track(self, distance): """ 获取滑块移动轨迹的列表 :param distance: 第二个缺块的左侧的x坐标 :return: 滑块移动轨迹列表 """ # 移动轨迹 track = [] # 当前位移 current = 0 # 减速阈值 mid = distance * 2 / 3 # 计算间隔 t = 0.4 # 初速度 v = 0 distance += 10 # 使滑块划过目标地点, 然后回退 while current < distance: # 加速度 if current < mid: a = 2 else: a = -3 # 初速度 v0 v0 = v # 当前速度 v = v0 + a * t # 移动距离 move = v0 * t + 0.5 * a * t * t # 当前位移 current += move track.append(move) return track def get_slider(self): return self.wait.until( EC.element_to_be_clickable( (By.CLASS_NAME, "geetest_slider_button"))) def move_to_gap(self, button, track): ActionChains(self.browser).click_and_hold(button).perform() for x in track: ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform() # time.sleep(0.5) # time.sleep(0.5) ActionChains(self.browser).release().perform() @seleniumLoopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 打开浏览器,并且输入账号密码 :return: None """ self.logger.info('尝试登录...') self.browser.get( "https://passport.zhaopin.com/login?bkUrl=%2F%2Fi.zhaopin.com%2Fblank%3Fhttps%3A%2F%2Fwww.zhaopin.com%2F" ) time.sleep(1) self.browser.find_element_by_xpath( '//li[@class="zppp-panel-tab"]').click() time.sleep(1) username = self.wait.until( EC.element_to_be_clickable( (By.CSS_SELECTOR, 'input[type="text"]'))) password = self.wait.until( EC.element_to_be_clickable( (By.CSS_SELECTOR, 'input[type="password"]'))) submit = self.wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, '.zppp-submit'))) time.sleep(1) username.clear() username.send_keys(self.username) time.sleep(1) password.clear() password.send_keys(self.password) time.sleep(1) self.logger.info('账号密码输入完成, 点击登录按钮') submit.click() self.logger.info('等待验证码加载...') image1 = self.get_geetest_image("captcha1.png", True) image2 = self.get_geetest_image("captcha2.png", False) gap = self.get_gap(image1, image2) track = self.get_track(gap - BORDER) slider = self.get_slider() self.logger.info('移动滑块至缺口...') self.move_to_gap(slider, track) time.sleep(3) # 跳转至首页则登录成功 self.logger.info('校验滑动验证是否成功...') if self.browser.current_url == 'https://www.zhaopin.com/': self.logger.info('校验完成, 登录成功! ') nickname = self.browser.find_element_by_css_selector( '.zp-userinfo').text self.logger.info('Hello, {}! '.format(nickname)) cookies = self.browser.get_cookies() cookies = {item['name']: item['value'] for item in cookies} self.redis_client.save_cookies(self.site, self.username, cookies) self.browser.close() return cookies elif self.wait.until( EC.presence_of_element_located( (By.CSS_SELECTOR, "canvas.geetest_canvas_slice"))): raise Exception('滑动验证失败! ') elif self.browser.find_element_by_xpath('//p[@class="tips"]'): self.logger.error('校验完成, 登录失败: {}! '.format( self.browser.find_element_by_xpath('//p[@class="tips"]').text)) return None raise Exception('登录失败! ') @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') options = webdriver.ChromeOptions() # 设置为开发者模式,避免被识别 options.add_experimental_option('excludeSwitches', ['enable-automation']) options.add_argument('--headless') self.browser = webdriver.Chrome(options=options) self.wait = WebDriverWait(self.browser, 20) cookies = self.login() self.logger.info('程序结束!') return cookies
class ZhihuLogin: def __init__(self, username: str = None, password: str = None): self.site = 'zhihu' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.login_data = { 'captcha': '', 'client_id': 'c3cef7c66a1843f8b3a9e6a1e3160e20', 'grant_type': 'password', 'lang': 'en', 'password': '', 'ref_source': 'homepage', 'signature': '', 'source': 'com.zhihu.web', 'timestamp': '', 'username': '', 'utm_source': '' } self.session = requests.session() self.session.headers = { 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'origin': 'www.zhihu.com', 'referer': 'https://www.zhihu.com/signin?next=%2F', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' '(KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): """ 检查登录状态,访问登录页面跳转至首页则是已登录, 如登录成功保存当前 Cookies :return: bool """ login_url = 'https://www.zhihu.com/api/v4/me?include=ad_type%2Cavailable_message_types%2Cdefault_notifications_count%2Cfollow_notifications_count%2Cvote_thank_notifications_count%2Cmessages_count%2Caccount_status%2Cemail%2Cis_bind_phone' resp = self.session.get(login_url, cookies=cookies) if 'error' not in resp.text: self.logger.info('登录成功! ') nickname = resp.json()['name'] self.logger.info('Hello, {}! '.format(nickname)) return True return False @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def _get_xsrf(self): """ 从登录页面获取 xsrf :return: str """ self.session.get('https://www.zhihu.com/', allow_redirects=False) for cookie in self.session.cookies: if cookie.name == '_xsrf': # print(cookie.value) return cookie.value raise AssertionError('获取 xsrf 失败') @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def _get_captcha(self, lang: str): """ 请求验证码的 API 接口,无论是否需要验证码都需要请求一次 如果需要验证码会返回图片的 base64 编码 根据 lang 参数匹配验证码,需要人工输入 :param lang: 返回验证码的语言(en/cn) :return: 验证码的 POST 参数 """ api = 'https://www.zhihu.com/api/v3/oauth/captcha?lang={}'.format(lang) resp = self.session.get(api) show_captcha = re.search(r'true', resp.text) if show_captcha: put_resp = self.session.put(api) json_data = json.loads(put_resp.text) img_base64 = json_data['img_base64'].replace(r'\n', '') img_data = base64.b64decode(img_base64) self.logger.info('使用超级鹰识别验证码...') ok, result = image_to_text(img_data) if ok: self.logger.info('成功识别验证码! ') # 这里必须先把参数 POST 验证码接口 time.sleep(1) res = self.session.post(api, data={ 'input_text': result }).json() if 'success' in res.keys(): return result raise Exception('验证码 post 失败: {}'.format( res['error']['message'])) raise Exception('验证码识别失败: ', result) raise Exception('获取验证码失败! ') @staticmethod def _get_signature(timestamp: int or str): """ 获取signature签名参数 :param timestamp: 时间戳 :return: 签名 """ with open('signature.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) signature = ctx.call('get_signature', timestamp) return signature # 直接用python内置Hmac加密算法如下, # def _get_signature(self, timestamp: int or str): # """ # 通过 Hmac 算法计算返回签名 # 实际是几个固定字符串加时间戳 # :param timestamp: 时间戳 # :return: 签名 # """ # hmac = hmac.new(b'd1b964811afb40118a12068ff74a12f4', digestmod=hashlib.sha1) # hmac.update(self.login_data['grant_type'].encode()) # hmac.update(self.login_data['client_id'].encode()) # hmac.update(self.login_data['source'].encode()) # hmac.update(str(timestamp).encode()) # signature = hmac.hexdigest() # # print(signature) # return signature @staticmethod def _encrypt(form_data): with open('get_formdata.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) data = ctx.call('encrypt', urlencode(form_data)) # print('加密后的data: ', data) return data @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self, captcha_lang: str = 'en'): """ 模拟登录知乎 :param captcha_lang: 验证码类型 'en' or 'cn' :param load_cookies: 是否读取上次保存的 Cookies :return: bool 若在 PyCharm 下使用中文验证出现无法点击的问题, 需要在 Settings / Tools / Python Scientific / Show Plots in Toolwindow,取消勾选 """ self.login_data.update({ 'username': self.username, 'password': self.password, 'lang': captcha_lang }) timestamp = int(time.time() * 1000) self.login_data.update({ 'captcha': self._get_captcha(self.login_data['lang']), 'timestamp': timestamp, 'signature': self._get_signature(timestamp) }) headers = self.session.headers.copy() headers.update({ 'content-type': 'application/x-www-form-urlencoded', 'x-zse-83': '3_2.0', 'x-xsrftoken': self._get_xsrf(), 'x-requested-with': 'fetch', 'x-ab-param': 'se_time_threshold=0;zr_album_chapter_exp=0;li_price_test=1;li_qa_cover=old;se_ltr_v008=0;top_ebook=0;tp_qa_metacard=1;top_vipconsume=1;tp_header_style=1;li_album_liutongab=0;li_qa_new_cover=0;se_famous=1;se_search_feed=N;zr_album_exp=0;zr_km_xgb_model=old;ls_fmp4=1;se_colorfultab=1;se_limit=0;tp_qa_toast=1;top_reason=1;se_college=default;se_payconsult_click=0;se_topicdirect=2;se_zu_onebox=0;pf_feed=1;se_ri=0;top_test_4_liguangyi=1;tp_qa_metacard_top=top;li_album3_ab=0;qa_test=0;se_college_cm=0;se_title_only=0;top_rank=0;tp_sft=a;tp_sft_v2= a;ug_zero_follow_0=0;se_backsearch=0;se_bl=0;se_page_limit_20=1;se_rr=0;soc_bigone=0;ug_follow_topic_1=2;zr_infinity_a_u=close;li_auif_ab=0;pf_fuceng=1;se_mobileweb=0;se_websearch=3;li_se_ebook_chapter=1;se_payconsult=0;se_subtext=0;se_wannasearch=0;qa_answerlist_ad=0;se_auto_syn=0;top_root=0;se_featured=1;soc_special=0;tsp_hotctr=1;li_ebook_detail=1;ug_zero_follow=0;zr_art_rec=base;zr_video_recall=current_recall;top_recall_exp_v1=1;pf_noti_entry_num=0;se_site_onebox=0;se_time_score=1;top_quality=0;pf_foltopic_usernum=50;se_billboardsearch=0;zr_km_answer=open_cvr;se_ios_spb309=0;se_lottery=0;se_terminate=0;top_recall_deep_user=1;ls_videoad=0;se_webrs=1;tp_m_intro_re_topic=1;zr_km_style=base;ug_follow_answerer=0;top_hotcommerce=1;top_new_feed=5;pf_newguide_vertical=0;zr_ans_rec=gbrank;li_tjys_ec_ab=0;se_spb309=0;se_whitelist=0;se_amovietab=0;top_native_answer=1;top_recall_exp_v2=1;ug_goodcomment_0=1;pf_creator_card=1;se_ad_index=10;ug_follow_answerer_0=0;ug_goodcomment=0;se_topic_pu=0;se_webtimebox=0;tp_meta_card=0;tsp_childbillboard=1;se_expired_ob=0;se_new_topic=0;se_pay_score=0;se_p_slideshow=0;zr_rel_search=base;zr_video_rank=current_rank;top_gr_ab=0;tp_sticky_android=0;tsp_lastread=0;ug_newtag=0;li_hot_score_ab=0;se_movietab=0;se_pyc_click2=1;soc_bignew=1;zr_ebook_chapter=0;zr_km_slot_style=event_card;ls_new_upload=0;se_preset_tech=0;ug_fw_answ_aut_1=0;top_universalebook=1;top_ydyq=X;li_mceb=0;li_ts_sample=old;se_agency= 0;se_waterfall=0;se_timebox_num=3;se_topic_express=0;se_zu_recommend=0;zr_se_footer=1;se_likebutton=0;se_webmajorob=0;soc_update=1;zr_infinity_xgb=top3;se_ltr_v002=1;top_v_album=1;zr_es_update=0', }) # 知乎的登录表单是加密后字符串作为键, 然后值为空的一个字典, 而不是直接用加密的字符串作为表单提交 data = {self._encrypt(self.login_data): ''} login_api = 'https://www.zhihu.com/api/v3/oauth/sign_in' resp = self.session.post(login_api, data=data, headers=headers) cookies = resp.cookies.get_dict() if self.check_islogin(cookies): return True elif resp.json()['error']['message'] == '帐号或密码错误' or resp.json( )['error']['message'] == '密码长度不足': self.reset_flag = True raise Exception('帐号或密码错误! ') raise Exception('登录失败: ', resp.json()['error']['message']) @check_user() def run(self, load_cookies: bool = True): if self.username.isdigit() and '+86' not in self.username: self.username = '******' + self.username if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期') self.login()
class City58Login: def __init__(self, username: str = None, password: str = None): self.site = '58city' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False def _get_token(self): """ 获取登录的token参数 :return: """ base_url = 'https://passport.58.com/sec/58/feature/pc/ui?' res = self.session.get(base_url) doc = pq(res.text) path = doc('#path').attr('value') timestamp = int(time.time() * 1000) url = f'https://passport.58.com/58/login/init?callback=jQuery112401218021763208601_{timestamp}&source=58-default-pc&path={path}&psdk-d=jsdk&psdk-v=1.0.0&_={int(time.time()*1000)}' r = self.session.get(url) result = json.loads( r.text.replace(f'jQuery112401218021763208601_{timestamp}(', '').replace(')', '')) token = result['data']['token'] return path, token def _encrypt_pwd(self): """ RSA加密密码 :return: """ with open('encrypt.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) encrypt_pwd = ctx.call('encryptString', self.password) return encrypt_pwd def check_islogin(self, cookies): """ 检查是否成功登录 :return: """ url = 'http://my.58.com/webpart/userbasicinfo?' res = self.session.get(url, cookies=cookies) if 'username' in res.text: self.logger.info('Cookies 有效!') nickname = unquote(cookies['58uname']) self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 登录 :return: """ login_api = 'https://passport.58.com/58/login/pc/dologin' encrypt_pwd = self._encrypt_pwd() path, token = self._get_token() # 浏览器指纹可固定 data = { 'fingerprint': 'cFJ17E_h2J-JDePNDcjQipipJf-ulq8O', 'callback': 'successFun', 'username': self.username, 'password': encrypt_pwd, 'token': token, 'source': '58-default-pc', 'path': path, 'domain': '58.com', 'finger2': 'zh-CN|24|1|4|1366_768|1366_728|-480|1|1|1|undefined|1|unknown|Win32|unknown|3|false|false|false|false|false|0_false_false|d41d8cd98f00b204e9800998ecf8427e|ab307cc3fc702e5ba66265525bf235e9', 'psdk-d': 'jsdk', 'psdk-v': '1.0.0' } self.session.headers.update({ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'cache-control': 'max-age=0', 'content-length': '918', 'content-type': 'application/x-www-form-urlencoded', 'cookie': 'id58=c5/njVzb4POxn3A0A5HAAg==; 58tj_uuid=d9c75b22-75df-45a5-9753-7d50573ecf07; als=0; xxzl_deviceid=q7tLhmL5%2BD1rBg1ATI6k4pOMk5JPQohVW%2F2gGSVpM6FQmGsn62qCn5qWyuyKi9WD; wmda_uuid=241b006d58daf7ba9c3bb7e99a44dc04; wmda_new_uuid=1; wmda_visited_projects=%3B2385390625025; ppStore_fingerprint=2435661B36DB2AADF42830177DB0924E0018B24F3003A472%EF%BC%BF1557913861099; 58home=hf; city=hf; mcity=hf; finger_session=cFJ17E_h2J-JDePNDcjQipipJf-ulq8O; new_session=1; new_uv=6; utm_source=sem-sales-360-pc; spm=18470355416.%7Bcreative%7D; init_refer=https%253A%252F%252Fwww.so.com%252Fs%253Fie%253Dutf-8%2526src%253Ddlm%2526shb%253D1%2526hsid%253D048b900bc5158a43%2526ls%253Dn5eecf99698%2526q%253D58%2525E5%252590%25258C%2525E5%25259F%25258E', 'origin': 'https://passport.58.com', 'referer': 'https://passport.58.com/login/?path=https%3A//hf.58.com/chuzu/%3Futm_source%3Dsem-sales-360-pc%26spm%3D18470355416.%7Bcreative%7D%26utm_campaign%3Dsell%26utm_medium%3Dcpc%26showpjs%3Dpc_fg&PGTID=0d3090a7-0034-5e39-c90b-41d899bb8a55&ClickID=2', 'upgrade-insecure-requests': '1', }) resp = self.session.post(login_api, data=data) doc = pq(resp.text) result = json.loads( re.search(r'parent.successFun\((.*?)\)', doc('script').text()).group(1)) if result['code'] == 0: self.logger.info('登录成功! ') cookies = resp.cookies.get_dict() nickname = unquote(cookies['58uname']) self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return cookies elif result['msg'] == '该用户名与密码不符' or result['msg'] == '密码格式错误,请重置': self.reset_flag = True raise Exception('账号或密码错误! ') # 手机号验证... raise Exception('登录失败: {} '.format(result['msg'])) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login()
class Email163Login: def __init__(self, username: str = None, password: str = None): self.site = '163email' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Content-Type': 'application/json', 'Origin': 'https://dl.reg.163.com', 'Referer': 'https://mail.163.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36', } with open('encryptEnvinfo.js', 'r') as f: self.js = f.read() self.ctx = execjs.compile(self.js) self.rtid = self.ctx.call('getRtid') # 密码错误重试初始化 self.reset_flag = False def check_islogin(self, cookies): sid = cookies['sid'] params = {'sid': sid} cookie = cookies['cookies'] resp = self.session.get('http://mail.163.com/js6/main.jsp?', params=params, cookies=cookie) if '收件箱' in resp.text: self.logger.info('Cookies 有效! ') try: nickname = re.search("'true_name':'(.*?)'", resp.text).group(1) self.logger.info('Hello, {}! '.format(nickname)) except: self.logger.info('你还没有设置昵称, 快去设置吧! ') return True return False def _set_data(self, func): """ 配置收件箱接口表单参数 :return: """ with open('data.html', 'r') as f: html = f.read() data_list = html.split('<?xml version="1.0"?>') if func == 'global:sequential': return data_list[1] elif func == 'mbox:listMessages': return data_list[2] elif func == 'mbox:readMessage': return data_list[3] else: self.logger.warning('不支持的接口类型, 有需要请自行添加! ') @staticmethod def _format_date(text): """ 格式化日期 :param date: :return: """ date = re.search(r"new Date\((.*?)\)", text, re.S) send_time = '-'.join([ x for x in date.group(1).split(',')[:3] ]) + ' ' + ':'.join([x for x in date.group(1).split(',')[3:]]) return text.replace(date.group(0), send_time) def _read_email(self, cookies): """ 查看收件箱: 接口返回的数据是字典样式的原生字符串, demjson 格式化后都无法转为 json 格式... 懒得用正则提取了, 将就看 :return: """ cookie = cookies['cookies'] sid = cookies['sid'] self.session.headers.update({ 'Accept': 'text/javascript', # 请求头携带这个参数接口返回的是格式化数据, 没有这个参数返回的html源码 'Content-type': 'application/x-www-form-urlencoded' }) url = 'https://mail.163.com/js6/s?' func_map = { '1': 'global:sequential', '2': 'mbox:listMessages', '3': 'mbox:readMessage' } sign_list = [] all_flag = input('是否查看邮箱整体信息? (yes/任意键跳过) >> \n') if all_flag == 'yes': all_flag = 1 else: all_flag = 0 if all_flag: sign_list.append('1') mailbox_flag = input('是否查看收件箱邮件列表? (yes/任意键跳过) >> \n') if mailbox_flag == 'yes': mailbox_flag = 1 else: mailbox_flag = 0 if mailbox_flag: sign_list.append('2') for sign in sign_list: params = { 'sid': sid, 'func': func_map[sign], } data = {'var': self._set_data(func_map[sign])} res = self.session.post(url, params=params, data=data, cookies=cookie) if sign == '1': self.logger.info('邮箱整体信息如下:') pprint(res.text) elif sign == '2': self.logger.info('收件箱邮件列表: ') ids = re.findall("'id':'(.*?)'", res.text) subjects = re.findall("'subject':'(.*?)'", res.text) id_dict = {str(index): id_ for index, id_ in enumerate(ids)} subject_dict = { str(index): subject for index, subject in enumerate(subjects) } if id_dict: pprint(subject_dict) view_flag = input('是否需要查看具体邮件信息? (yes/任意键退出) >> \n') if view_flag == 'yes': view_flag = 1 else: view_flag = 0 while view_flag: email_num = input('请输入邮件编号(左边数字)>> ') params = { 'sid': sid, 'func': func_map['3'], } origin_id = re.search('"id">(.*?)<', self._set_data( func_map['3'])).group(1) data = { 'var': self._set_data(func_map['3']).replace( origin_id, id_dict[email_num]) } resp = self.session.post(url, params=params, data=data, cookies=cookie) result = self._format_date(resp.text) pprint(result) view_flag = int(input('继续查看请按 1 , 退出程序请按 0 >> \n')) else: self.logger.info('收件箱列表空空如也~ ') def _init_cookies(self): """ 初始化 Cookies, 关键 Cookie: l_s_mail163CvViHzl, 有这个 Cookie 拿到 tk :return: """ url = 'https://dl.reg.163.com/dl/ini?' params = { "pd": "mail163", "pkid": "CvViHzl", "pkht": "mail.163.com", "channel": "0", "topURL": "https://mail.163.com/", "rtid": self.rtid, "nocache": int(time.time() * 1000) } self.session.get(url, params=params) def _get_token(self): """ 获取页面 token :return: """ url = 'https://dl.reg.163.com/gt?' params = { 'un': self.username, 'pkid': 'CvViHzl', 'pd': 'mail163', 'channel': 0, 'topURL': 'https://mail.163.com/', 'rtid': self.rtid, 'nocache': int(time.time() * 1000) } result = self.session.get(url, params=params).json() token = result['tk'] return token def _encrypt_pwd(self): """ 加密密码 :return: """ return self.ctx.call('encrypt2', self.password) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): self._init_cookies() tk = self._get_token() login_api = 'https://dl.reg.163.com/dl/l' data = { "un": self.username, "pw": self._encrypt_pwd(), "pd": "mail163", "l": 0, "channel": 0, "d": 10, "t": int(time.time() * 1000), "pkid": "CvViHzl", "domains": "", "tk": tk, "pwdKeyUp": 1, "topURL": "https://mail.163.com/", "rtid": self.rtid } self.session.headers.update({ 'Cookie': 'JSESSIONID-WYTXZDL=g1JHfvz6wsLNeHAdyUtE0FRiruebrGhpnsGt%5CTZs%5CPAAfGmXG5hGzE3eEu0QDmcqGQKf87EVpesHEvrEGPWeWs4u%2FAgfTWKfoTE3GpnbrQtrto1LU0B2f9tlUNun7en3J7p1PFX6ddMsK%2Bh0LlsLVf5MAN6qTzfWGo5jB6tm5KnpbBdo%3A1569055359975; l_s_mail163CvViHzl=2BDA1093FDDA9283AD02B57FFFEC7E0EF7CE8F4A6E2DA32BBA32CC50C8A54E5BB98676C37C29B6F138D45F2B1D5510896F1359E92D0A78847C03BEBD800E91DCC6880B78017150B4FBBD720B8AE3FE99EE370B654693348BE2A23B1B22020FC24086B6B03057ED5D1AD40CEA84949569' }) res = self.session.post(login_api, data=json.dumps(data)).json() if res['ret'] == '201': form_data = { "style": "-1", "df": "mail163_letter", "allssl": "true", "net": "", "language": "-1", "from": "web", "race": "", "iframe": "1", "url2": "https://mail.163.com/errorpage/error163.htm", "product": "mail163" } cookies = 'df=mail163_letter; NTES_SESS=' + self.session.cookies.get_dict( )['NTES_SESS'] # cookies = '; '.join([key + '=' + value for key, value in self.session.cookies.get_dict().items()]) self.session.headers.update({'Cookie': cookies}) resp = self.session.post( 'https://mail.163.com/entry/cgi/ntesdoor?', data=form_data, allow_redirects=False) redirect_url = resp.headers['location'].replace( 'unknow', 'mail163_letter') if 'sid' in redirect_url: self.logger.info('登录成功! ') response = self.session.get(redirect_url) # 网易邮箱的关键是这个sid, 是 session id 的意思, 有了它就可以爬邮箱了, 当然还要有配套的 Cookie cookies_item = { 'cookies': resp.cookies.get_dict(), 'sid': redirect_url.split('?')[1].split('&')[0].split('=')[1] } self.redis_client.save_cookies(self.site, self.username, cookies_item) try: nickname = re.search("'true_name':'(.*?)'", response.text).group(1) self.logger.info('Hello, {}! '.format(nickname)) except: self.logger.info('你还没有设置昵称, 快去设置...') return cookies_item raise Exception('登录失败! ') elif res['ret'] == '413': self.reset_flag = True raise Exception('账号或密码错误! ') elif res['ret'] == '445': self.logger.warning('验证码校验...') return None elif res['ret'] == '409': self.logger.warning('登录过于频繁, 请稍后再试! ') return None elif res['ret'] == '423': self.logger.warning('风控账号! ') return None raise Exception('登录失败! ') @check_user() def run(self, load_cookies: bool = True): if '163.com' not in self.username: self.username += '@163.com' if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): read_flag = input('是否需要查看邮箱? (yes/任意键退出) >> \n') if read_flag == 'yes': self._read_email(cookies) return cookies self.logger.warning('Cookies 已过期') return self.login()
class ToutiaoLogin: def __init__(self, username: str = None, password: str = None, headless=False): self.site = 'toutiao' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.browser = None self.wait = None self.headless = headless # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): """ 检查登录状态, 请求网站首页出现用户名则 cookies 有效 :param cookies: :return: """ url = 'https://www.toutiao.com/' headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } resp = requests.get(url, headers=headers, cookies=cookies) nickname = re.search("userName: '******',", resp.text).group(1) if nickname != '': self.logger.info('Cookies 有效! ') self.logger.info('Hello, {}! '.format(nickname)) return True return False def set_browser(self): """ 配置 selenium :return: """ options = webdriver.ChromeOptions() # 设置为开发者模式,避免被识别, 开发者模式下 webdriver 属性为 undefined options.add_experimental_option('excludeSwitches', ['enable-automation']) if self.headless: options.add_argument('--headless') self.browser = webdriver.Chrome(options=options) self.wait = WebDriverWait(self.browser, 20) @staticmethod def pic_download(url, type): """ 下载验证码图片 :param url: :param type: :return: """ img_data = requests.get(url).content with open('./{}.jpg'.format(type), 'wb') as f: f.write(img_data) @staticmethod def process_img(img1, img2): """ 图片处理 :param img1: 处理后图片 :param img2: 待处理图片 :return: """ cv2.imwrite(img1, img2) target = cv2.imread(img1) target = cv2.cvtColor(target, cv2.COLOR_BGR2GRAY) target = abs(255 - target) cv2.imwrite(img1, target) def get_distance(self, slider_url, captcha_url): """ 获取缺口距离 :param slider_url: 滑块图片 url :param captcha_url: 验证码图片 url :return: """ # 引用上面的图片下载 self.pic_download(slider_url, 'slider') time.sleep(2) # 引用上面的图片下载 self.pic_download(captcha_url, 'captcha') # 计算拼图还原距离 target = cv2.imread('slider.jpg', 0) template = cv2.imread('captcha.jpg', 0) w, h = target.shape[::-1] temp = 'temp.jpg' targ = 'targ.jpg' self.process_img(temp, template) self.process_img(targ, target) target = cv2.imread(targ) template = cv2.imread(temp) result = cv2.matchTemplate(target, template, cv2.TM_CCOEFF_NORMED) x, y = np.unravel_index(result.argmax(), result.shape) # 缺口位置 # print((y, x, y + w, x + h)) # 调用PIL Image 做测试 image = Image.open('captcha.jpg') xy = (y, x, y + w, x + h) # 切割 imagecrop = image.crop(xy) # 保存切割的缺口 imagecrop.save("new_image.jpg") # imagecrop.show() return y def get_slider(self): """ 获取滑块 :return: """ return self.wait.until( EC.element_to_be_clickable( (By.XPATH, '//img[@class="drag-button"]'))) def move_to_gap(self, distance, slider): """ 移动滑块至缺口 :param distance: 缺口距离 :param slider: 滑块对象 :return: """ has_gone_dist = 0 remaining_dist = distance # distance += randint(-10, 10) # 按下鼠标左键 ActionChains(self.browser).click_and_hold(slider).perform() time.sleep(0.5) while remaining_dist > 0: ratio = remaining_dist / distance if ratio < 0.1: # 开始阶段移动较慢 span = random.randint(3, 5) elif ratio > 0.9: # 结束阶段移动较慢 span = random.randint(5, 8) else: # 中间部分移动快 span = random.randint(15, 20) ActionChains(self.browser).move_by_offset(span, random.randint( -5, 5)).perform() remaining_dist -= span has_gone_dist += span time.sleep(random.randint(5, 20) / 100) ActionChains(self.browser).move_by_offset(remaining_dist, random.randint(-5, 5)).perform() ActionChains(self.browser).release(on_element=slider).perform() def is_element_exists(self, element): """ 判断页面元素是否存在: Xpath :param element: :return: """ try: self.browser.find_element_by_xpath(element) return True except: return False @seleniumLoopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 打开浏览器,并且输入账号密码 :return: """ self.logger.info('尝试登录...') self.browser.get('https://sso.toutiao.com/') self.logger.info('点击选择账号密码登录...') acount_login = self.wait.until( EC.presence_of_element_located( (By.XPATH, '//div[@id="login-type-account"]'))) acount_login.click() self.logger.info('输入账号...') input_username = self.wait.until( EC.presence_of_element_located((By.XPATH, '//*[@id="user-name"]'))) input_username.send_keys(self.username) time.sleep(1) self.logger.info('输入密码...') input_password = self.wait.until( EC.presence_of_element_located((By.XPATH, '//*[@id="password"]'))) input_password.send_keys(self.password) time.sleep(1) self.logger.info('点击登录...') button = self.wait.until( EC.presence_of_element_located( (By.XPATH, '//*[@id="bytedance-login-submit"]'))) button.click() time.sleep(3) # 判断是否出现滑块验证 slider_flag = self.is_element_exists('//*[@id="verify-bar-box"]') if not slider_flag: self.logger.info('未出现滑块验证! ') time.sleep(3) error_flag = self.is_element_exists('//div[@class="login-msg"]') if error_flag: msg_error = self.browser.find_element_by_xpath( '//div[@class="login-msg"]').text if '帐号或密码错误' in msg_error: self.reset_flag = True raise Exception('账号或密码错误! ') else: self.logger.error(msg_error) return None elif self.browser.current_url != "https://www.toutiao.com/": self.logger.error('登录失败! ') return None else: self.logger.info('登录成功! ') cookies = self.browser.get_cookies() cookies = { cookie['name']: cookie['value'] for cookie in cookies } self.redis_client.save_cookies(self.site, self.username, cookies) return cookies self.logger.info('出现滑块验证! ') while True: captcha_url = self.wait.until( EC.presence_of_element_located( (By.XPATH, '//*[@id="validate-big"]'))).get_attribute('src') slider_url = self.wait.until( EC.presence_of_element_located( (By.XPATH, '//img[@class="validate-block"]'))).get_attribute('src') distance = self.get_distance(slider_url, captcha_url) slider = self.get_slider() self.move_to_gap(distance, slider) time.sleep(3) error_flag = self.is_element_exists('//div[@class="login-msg"]') if error_flag: self.logger.info('验证通过! ') msg_error = self.browser.find_element_by_xpath( '//div[@class="login-msg"]').text if '帐号或密码错误' in msg_error: self.reset_flag = True raise Exception('账号或密码错误! ') else: self.logger.error(msg_error) return msg_error elif self.is_element_exists('//*[@id="verify-bar-box"]'): self.logger.warning('验证失败, 重试! ') time.sleep(0.5) else: self.logger.info('验证通过, 登录成功! ') cookies = self.browser.get_cookies() cookies = { cookie['name']: cookie['value'] for cookie in cookies } self.redis_client.save_cookies(self.site, self.username, cookies) return cookies time.sleep(random.randint(1, 3)) @check_user() def run(self, load_cookies: bool = True): """ 运行 :param load_cookies: 是否加载数据库中的 cookies :return: cookies """ if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') self.set_browser() cookies = self.login() self.logger.info('程序结束! ') return cookies
class SteamLogin: def __init__(self, username: str = None, password: str = None): self.site = 'steam' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): """ 检查是否登录成功 :return: """ res = self.session.get('https://store.steampowered.com/', cookies=cookies) if 'account_pulldown' in res.text: bsobj = BeautifulSoup(res.text, 'lxml') nickname = bsobj.select('#account_pulldown')[0].get_text() self.logger.info('Cookies 有效! ') self.logger.info(f'Hello, {nickname}! ') return True return False def _init_cookies(self): self.session.get('https://store.steampowered.com/') def _get_rsakey(self): """ 获取 RSA 加密参数 :return: """ url = 'https://store.steampowered.com/login/getrsakey/' data = { 'donotcache': int(time.time() * 1000), 'username': self.username } res = self.session.post(url, data=data).json() rsa_mod = res['publickey_mod'] rsa_exp = res['publickey_exp'] rsa_timestamp = res['timestamp'] return rsa_mod, rsa_exp, rsa_timestamp def _encrypt_pwd(self, rsa_mod, rsa_exp): with open('encryptPwd.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) return ctx.call('encrypt', self.password, rsa_mod, rsa_exp) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): login_api = 'https://store.steampowered.com/login/dologin/' self._init_cookies() rsa_mod, rsa_exp, rsa_timestamp = self._get_rsakey() pwd = self._encrypt_pwd(rsa_mod, rsa_exp) data = { 'donotcache': int(time.time() * 1000), 'password': pwd, 'username': self.username, 'twofactorcode': '', 'emailauth': '', 'loginfriendlyname': '', 'captchagid': '-1', 'captcha_text': '', 'emailsteamid': '', 'rsatimestamp': rsa_timestamp, 'remember_login': '******' } res = self.session.post(login_api, data=data).json() if res['success']: transfer_urls = res['transfer_urls'] transfer_parameters = res['transfer_parameters'] resp = self.session.post(transfer_urls[1], data=transfer_parameters) if 'transfer_success' in resp.text: self.logger.info('登录成功! ') cookies = self.session.cookies.get_dict() self.redis_client.save_cookies(self.site, self.username, cookies) return cookies raise Exception('登录失败! ') else: if res['captcha_needed']: self.logger.info('此次登录需要验证码! ') return None elif res['message'] == '您输入的帐户名称或密码错误。' or \ res['message'] == 'The account name or password that you have entered is incorrect.': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败! ') @check_user() def run(self, load_cookies: bool = True): """ 主函数运行 :return: """ if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期! ') return self.login()
class HuyaLogin: def __init__(self, username: str = None, password: str = None): self.site = 'huya' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'content-type': 'application/json;charset=UTF-8', 'lcid': '2052', 'uri': '30001', 'Origin': 'https://udblgn.huya.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): """ 检查登录, 请求用户主页若出现用户名则登录成功 :return: """ res = self.session.get('https://i.huya.com/', cookies=cookies) bsobj = BeautifulSoup(res.text, 'lxml') if bsobj.find('h2', {'class': 'uesr_n'}): nickname = bsobj.find('h2', {'class': 'uesr_n'}).get_text() self.logger.info('登录成功! ') self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False def _get_sdid(self): url = 'https://udblgn.huya.com/web/middle/2.3/37893475/https/787b6ffa5e4c42a99091ab91d071ed2a' resp = self.session.get(url) sdid = re.search(r'HyUDBWebSDK_Exchange.init\((.*?)\);', resp.text).group(1) return sdid def encrypt(self): with open('encrypt.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) password = ctx.call('encryptPwd', self.password) request_id = ctx.call('getRequestId', 1) page = ctx.call('getPage', 'https://www.huya.com/l') # 页面cookie, 没有测试过期时间 context = ctx.call( 'getContext', '__yamid_tt1=0.1232795775887836; __yamid_new=C875D5606C80000176AD12EEAE0014D2; SoundValue=0.50; alphaValue=0.80; guid=3ad7b83861a9ea5c33512be62ce6b2fa; Hm_lvt_51700b6c722f5bb4cf39906a596ea41f=1558882650,1559909095,1559998946; __yasmid=0.1232795775887836; udb_passdata=3; isInLiveRoom=true; udb_guiddata=787b6ffa5e4c42a99091ab91d071ed2a; web_qrlogin_confirm_id=bf407b38-0a04-453a-8899-c93acf12f406; h_unt=1560003929; Hm_lpvt_51700b6c722f5bb4cf39906a596ea41f=1560003939; __yaoldyyuid=; _yasids=__rootsid%3DC87A02DEAC400001CC501DC016A81329; PHPSESSID=qdd0udkruqk8mi2u0unr4p53r5' ) return password, request_id, page, context @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): sdid = self._get_sdid() password, request_id, page, context = self.encrypt() self.session.headers.update({ 'context': context, 'reqid': str(request_id), 'Referer': 'https://udblgn.huya.com/web/middle/2.3/854732/https/{}'.format( context.split('-')[1]) }) payload = { 'appId': "5002", 'byPass': "******", 'context': context, 'data': { 'userName': "******", 'password': password, 'domainList': "", 'behavior': "%5B%7B%22page.login%22%3A%220.073%22%7D%2C%7B%22input.l.account%22%3A%222.856%22%7D%2C%7B%22input.l.passwd%22%3A%225.37%22%7D%2C%7B%22button.UDBSdkLogin%22%3A%228.387%2C138%2C254%22%7D%5D", 'page': page, 'randomStr': "", 'remember': "1" }, 'lcid': "2052", 'requestId': str(request_id), 'sdid': str(sdid), 'smid': "", 'uri': "30001", 'version': "2.4" } url = 'https://udblgn.huya.com/web/v2/passwordLogin' res = self.session.post(url, data=json.dumps(payload)) cookies = res.cookies.get_dict() if self.check_islogin(cookies): return cookies elif res.json()['description'] == '账号或密码错误': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: {}'.format(res.json()['description'])) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login()
class WangyiyunLogin: def __init__(self, username: str = None, password: str = None): self.site = 'wangyiyun' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): res = requests.get('https://music.163.com/#/user/home?', cookies=cookies) # 注: 正则拿到的 json 字符串中 key 没有被括起来(json 默认字典里的字符串为双引号括起来), 转 json 会报错, demjson 库可以修改 json 格式错误 json_str = re.search('var GUser=(.*?);', res.text, re.S).group(1) user_info = demjson.decode(json_str) if user_info: self.logger.info('登录成功! ') self.logger.info('Hello, {}! '.format(user_info['nickname'])) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False def get_wm_did(self): """ 获取 WM_DID, js生成认证 checkToken 所需的一个参数, 未找到 js 生成函数, 所以直接用浏览器获取了... 有效期是一天左右, 过期重新获取, 并保存到本地 txt 文件中, 避免每次登录都要重新调用浏览器获取 :return: """ options = webdriver.ChromeOptions() # 设置为开发者模式,避免被识别, 开发者模式下 webdriver 属性为 undefined options.add_experimental_option('excludeSwitches', ['enable-automation']) options.add_argument('--headless') browser = webdriver.Chrome(options=options) browser.get("https://music.163.com/#/login") time.sleep(1) WM_DID = browser.execute_script('return window.localStorage["WM_DID"]') param = WM_DID.split('__')[0] stime = WM_DID.split('__')[2][:10] etime = WM_DID.split('__')[1][:10] start_time = time.strftime( '%Y-%m-%d %H:%M:%S', time.localtime(int(WM_DID.split('__')[2][:10]))) end_time = time.strftime( '%Y-%m-%d %H:%M:%S', time.localtime(int(WM_DID.split('__')[1][:10]))) self.logger.info('{} 有效时间: {} - {}'.format(param, start_time, end_time)) browser.close() with open('param.txt', 'w') as f: f.write(param + '\n') f.write(stime + '\n') f.write(etime) return param def get_checktoken(self): """ 获取 token 认证 :return: """ with open('param.txt', 'r') as f: lines = f.readlines() param = lines[0].replace('\n', '') stime = int(lines[1].replace('\n', '')) etime = int(lines[2]) current_time = int(time.time()) if stime < current_time < etime: self.logger.info('{} 有效! '.format(param)) else: self.logger.warning('{} 已过期, 等待访问首页重新获取! '.format(param)) param = self.get_wm_did() with open('get_checktoken.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) return ctx.call('getCheckToken', param) def md5_encrypt(self): """ md5加密密码 :return: """ md5 = hashlib.md5() md5.update(self.password.encode()) encrypt_pwd = md5.hexdigest() return encrypt_pwd @staticmethod def get_random_str(): """ 获取一个随机16位字符串 :return: 随机16位字符串 """ str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' res = '' for x in range(16): index = math.floor(random.random() * len(str)) res += str[index] return res @staticmethod def aes_encrypt(text, key): """ AES加密 :param text: 待加密密文 :param key: 密钥 :return: """ # iv: 偏移量 iv = '0102030405060708' # 注:AES只能加密数字和字母,无法加密中文。 # 解决方法:在CBC加密模式下,字符串必须补齐长度为16的倍数,且长度指标不能为中文,需转化为unicode编码长度 pad = 16 - len(text.encode()) % 16 text = text + pad * chr(pad) encryptor = AES.new(key, AES.MODE_CBC, iv) # 最后还需要进行base64加密 msg = base64.b64encode(encryptor.encrypt(text)) return msg @staticmethod def rsa_encrypt(value, text, modulus): """ RSA加密 :param value: 加密指数 :param text: 待加密密文 :param modulus: 加密系数 :return: """ text = text[::-1] rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16)**int( value, 16) % int(modulus, 16) return format(rs, 'x').zfill(256) def get_data(self): """ params:进行了两次AES加密 encSecKey:进行了一次RSA加密 :return: """ encrypt_pwd = self.md5_encrypt() checkToken = self.get_checktoken() data = { "phone": self.username, "password": encrypt_pwd, "rememberLogin": "******", "checkToken": checkToken, "csrf_token": "" } text = json.dumps(data) random_text = self.get_random_str() # params: 两次AES加密 params = self.aes_encrypt(text, '0CoJUm6Qyw8W8jud') params = self.aes_encrypt(params.decode('utf-8'), random_text).decode('utf-8') # RSA加密系数, 固定值 module = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5a" \ "a76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46be" \ "e255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7" # encSeckey: 一次 RSA 加密, '010001' 为加密指数, 固定值 encSecKey = self.rsa_encrypt('010001', random_text, module) return {'params': params, 'encSecKey': encSecKey} @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): login_api = 'https://music.163.com/weapi/login/cellphone?csrf_token=' data = self.get_data() headers = { 'referer': 'https://music.163.com/', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } res = requests.post(login_api, data=data, headers=headers) cookies = res.cookies.get_dict() if res.json()['code'] == 200: self.logger.info('登录成功! ') self.logger.info('Hello, {}!'.format( res.json()['profile']['nickname'])) self.redis_client.save_cookies(self.site, self.username, cookies) return True elif res.json()['message'] == '密码错误': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: {} '.format(res.json()['message'])) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期') self.login()
class BaiduLogin: def __init__(self, username: str = None, password: str = None): self.site = 'baidu' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/601.2.7 (KHTML, like Gecko) Version/9.0.1 Safari/601.2.7', 'Referer': 'https://pan.baidu.com/', } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): """ ... :param cookies: :return: """ pass def _init_cookies(self): """ 访问百度网盘首页初始化 cookies :return: """ self.session.get('https://pan.baidu.com/') @staticmethod def _get_gid(): return str(uuid4()).upper() def _get_token(self, gid): """ 获取登录 token 认证 :return: """ url = 'https://passport.baidu.com/v2/api/?getapi' params = { 'getapi': '', 'tpl': 'mn', 'apiver': 'v3', 'tt': str(int(time.time() * 1000)), 'class': 'login', 'gid': gid, 'loginversion': 'v4', 'logintype': 'dialogLogin', 'traceid': '', 'callback': 'bd__cbs__pivyke', } resp = self.session.get(url=url, params=params) js = parse_json(resp.text.replace("\'", "\"")) return js['data']['token'] def _get_public_key(self, gid, token): """ 获取 RSA 加密公钥 :return: """ url = 'https://passport.baidu.com/v2/getpublickey' params = { 'token': token, 'tpl': 'mn', 'apiver': 'v3', 'tt': str(int(time.time() * 1000)), 'gid': gid, 'loginversion': 'v4', 'traceid': '', 'callback': 'bd__cbs__h02h0j' } resp = self.session.get(url=url, params=params) js = parse_json(resp.text.replace("\'", "\"")) key, public_key = js.get('key'), js.get('pubkey') return key, public_key def _encrypt_pwd(self, public_key): rsa_key = RSA.importKey(public_key) encryptor = Cipher_pkcs1_v1_5.new(rsa_key) cipher = b64encode(encryptor.encrypt(self.password.encode('utf-8'))) return cipher.decode('utf-8') def _get_verifycode(self, code_string): """ 使用超级鹰识别验证码 :param pcid: :return: """ captcha_url = f'https://passport.baidu.com/cgi-bin/genimage?{code_string}' img_data = self.session.get(captcha_url).content self.logger.info('使用超级鹰识别验证码...') ok, result = image_to_text(img_data) if ok: self.logger.info('成功识别验证码!') return result raise Exception('验证码识别失败: ', result) def _verify_phone(self, authtoken, lstr, ltoken, loginproxy): """ 手机验证 :return: """ url = 'https://passport.baidu.com/v2/sapi/authwidgetverify?' params = { "authtoken": authtoken, "type": "mobile", "jsonp": "1", "apiver": "v3", "verifychannel": "", "action": "getapi", "vcode": "", "questionAndAnswer": "", "needsid": "", "rsakey": "", "countrycode": "", "subpro": "", "u": "https://www.baidu.com/", "lstr": lstr, "ltoken": ltoken, "tpl": "mn", "winsdk": "", "authAction": "", "traceid": "00BC1501", "callback": "bd__cbs__h3w9ui" } res = self.session.get(url, params=params) encode_str = res.text.replace( 'bd__cbs__h3w9ui(', '').replace(')', '').replace('{', '').replace( '}', '').replace('"', '').replace("'", '').replace(' ', '') result = { item.split(':')[0]: item.split(':')[1] for item in encode_str.split(',') } if result['errno'] == '110000': self.logger.info('成功请求验证码接口! ') params.update({ "action": "send", }) resp = self.session.get(url, params=params) encode_str = resp.text.replace('bd__cbs__h3w9ui(', '').replace( ')', '').replace('{', '').replace('}', '').replace('"', '').replace( "'", '').replace(' ', '') result = { item.split(':')[0]: item.split(':')[1] for item in encode_str.split(',') } if result['errno'] == '110000': self.logger.info('验证码发生中, 请注意接收...') time.sleep(1) verify_code = input('请输入验证码 >> \n') params.update({ "action": "check", "vcode": verify_code, }) respo = self.session.get(url, params=params) encode_str = respo.text.replace( 'bd__cbs__h3w9ui(', '').replace(')', '').replace('{', '').replace('}', '').replace( '"', '').replace("'", '').replace(' ', '') result = { item.split(':')[0]: item.split(':')[1] for item in encode_str.split(',') } if result['errno'] == '110000': self.logger.info('手机验证成功! ') response = self.session.get(loginproxy) print(response.text) return True elif result['errno'] == '110002': self.logger.error(result['msg']) return False @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): self._init_cookies() gid = self._get_gid() token = self._get_token(gid) key, public_key = self._get_public_key(gid, token) pwd = self._encrypt_pwd(public_key) login_api = 'https://passport.baidu.com/v2/api/?login' data = { 'staticpage': 'https://www.baidu.com/cache/user/html/v3Jump.html', 'charset': 'UTF-8', 'token': token, 'tpl': 'netdisk', 'subpro': 'netdisk_web', 'apiver': 'v3', 'tt': str(int(time.time() * 1000)), 'codestring': '', 'safeflg': '0', 'u': 'https://www.baidu.com/', 'isPhone': 'false', 'detect': '1', 'gid': gid, 'quick_user': '******', 'logintype': 'dialogLogin', 'logLoginType': 'pc_loginDialog', 'idc': '', 'loginmerge': 'true', 'splogin': '******', 'username': self.username, 'password': pwd, 'rsakey': key, 'crypttype': '12', 'ppui_logintime': 389548, 'countrycode': '', 'loginversion': 'v4', 'traceid': '', 'callback': 'parent.bd__pcbs__oxzeyj' } for _ in range(10): resp = self.session.post(login_api, data=data) result_str = re.search(r'.*href \+= "(.*)"\+accounts', resp.text).group(1) result = { x.split('=')[0]: x.split('=')[1] for x in result_str.split('&') } if result['err_no'] == '0': self.logger.info('登录成功! ') cookies = resp.cookies.get_dict() self.redis_client.save_cookies(self.site, self.username, cookies) return True elif result['err_no'] in {'6', '257'}: code_str = result.get('codeString') self.logger.warning('请输入验证码! ') verify_code = self._get_verifycode(code_str) data.update({ 'codestring': code_str, 'verifycode': verify_code }) elif result['err_no'] == '120021': self.logger.warning('账号存在风险, 请进行手机验证! ') authtoken = result['authtoken'] lstr = result['lstr'] ltoken = result['ltoken'] loginproxy = result['loginproxy'] flag = self._verify_phone(authtoken, lstr, ltoken, loginproxy) if not flag: return elif result['err_no'] in {'4', '7'}: self.reset_flag = True raise Exception('账号或密码错误! ') @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期') self.login()
class ShixiSengLogin: def __init__(self, username: str = None, password: str = None): # 网站 self.site = 'shixiseng' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Referer': 'https://www.shixiseng.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置 self.reset_flag = False def check_islogin(self, cookies): res = self.session.get('https://www.shixiseng.com/', cookies=cookies) html = res.content.decode(chardet.detect(res.content)['encoding']) bsobj = BeautifulSoup(html, 'lxml') if bsobj.find('span', {'class': 'nickname'}): nickname = bsobj.find('span', { 'class': 'nickname' }).get_text().strip() self.logger.info('登录成功!') self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False def _encrypt_pwd(self): with open('encrypt.js', 'r') as f: js = f.read() ctx = execjs.compile(js) return ctx.call('myencode', self.password) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): pwd = self._encrypt_pwd() url = 'https://www.shixiseng.com/user/login' data = { 'username': self.username, 'password': pwd, 'remember_login': '******' } res = self.session.post(url, data=data) result = json.loads(res.text) cookies = res.cookies.get_dict() if self.check_islogin(cookies): return cookies elif result['msg'] == '密码不对' or '密码不正确': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: ', result['msg']) @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login()
class RenrenLogin: def __init__(self, username: str = None, password: str = None): self.site = 'renren' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } with open('encryptPwd.js', 'rb') as f: js = f.read().decode() self.ctx = execjs.compile(js) # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): """ 检查是否登录成功 :return: """ res = self.session.get('http://www.renren.com/home', cookies=cookies) if 'hd-name' in res.text: bsobj = BeautifulSoup(res.text, 'lxml') nickname = bsobj.select('.hd-name')[0].get_text() self.logger.info('Cookies 有效! ') self.logger.info(f'Hello, {nickname}! ') return True return False def _init_cookies(self): """ 访问首页初始化 Cookie :return: """ self.session.get('http://www.renren.com/') def _get_rkey(self): """ 获取加密所需密钥和 rkey 参数 :return: """ url = 'http://login.renren.com/ajax/getEncryptKey' res = self.session.get(url).json() if res['isEncrypt']: iv = res['e'] encryptKey = res['n'] rkey = res['rkey'] return iv, encryptKey, rkey raise Exception('获取密钥出错! ') def _encrypt_pwd(self, iv, encryptKey): return self.ctx.call('encrypt', self.password, iv, encryptKey) def _get_uniquetimestamp(self): return self.ctx.call('getUniqueTimestamp') def _show_captcha(self): """ 访问接口判断是否需要验证码 :return: """ url = 'http://www.renren.com/ajax/ShowCaptcha' data = {'email': self.username, '_rtk': 'a0cdef52'} res = self.session.post(url, data=data) if '0' in res.text: self.logger.info('此次登录无需验证码! ') return False return True def _get_verifycode(self): captcha_url = 'http://icode.renren.com/getcode.do?t=web_login&rnd=0.28838133194471105' img_data = self.session.get(captcha_url).content self.logger.info('使用超级鹰识别验证码...') ok, result = image_to_text(img_data) if ok: self.logger.info('成功识别验证码!') return result raise Exception('验证码识别失败: ', result) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): params = {'1': 1, 'uniqueTimestamp': self._get_uniquetimestamp()} login_api = 'http://www.renren.com/ajaxLogin/login?' + urlencode( params) self._init_cookies() iv, encryptKey, rkey = self._get_rkey() pwd = self._encrypt_pwd(iv, encryptKey) flag = self._show_captcha() if flag: icode = self._get_verifycode() else: icode = '' data = { 'email': self.username, 'icode': icode, 'origURL': 'http://www.renren.com/home', 'domain': 'renren.com', 'key_id': '1', 'captcha_type': 'web_login', 'password': pwd, 'rkey': rkey, 'f': 'http%3A%2F%2Fwww.renren.com%2F971788971' } self.session.headers['Referer'] = 'http://www.renren.com/SysHome.do' res = self.session.post(login_api, data=data) if 'failCode' not in res.text: self.logger.info('登录成功! ') resp = self.session.get('http://www.renren.com/home', allow_redirects=False) redirect_url = resp.headers['location'] cookies = self.session.cookies.get_dict() # 巨坑... 这里返回的 cookies 中的 t 的值需要替换为 societyguester 的值 # 真正起作用的 cookie 就是 t这个键 和 societyguester 的值, 其他 cookie 可以不要。 cookies['t'] = cookies['societyguester'] self.redis_client.save_cookies(self.site, self.username, cookies) response = self.session.get(redirect_url) bsobj = BeautifulSoup(response.text, 'lxml') nickname = bsobj.select('.hd-name')[0].get_text() self.logger.info('Hello, {}! '.format(nickname)) return True elif res.json()['failCode'] == 128: self.reset_flag = True raise Exception('账号或密码错误! ') elif res.json()['failCode'] == 512: raise Exception(res.json()['failDescription']) raise Exception('登录失败! ') @check_user() def run(self, load_cookies: bool = True): """ 主函数运行 :return: """ if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.warning('Cookies 已过期! ') self.login()
class MeituanLogin: def __init__(self, username: str = None, password: str = None): self.site = 'meituan' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): url = 'https://www.meituan.com/ptapi/getLoginedUserInfo?timestamp={}'.format(int(time.time() * 1000)) headers = { 'Referer': 'http://hf.meituan.com/', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } cookies = {item['name']: item['value'] for item in cookies} resp = requests.get(url, headers=headers, cookies=cookies).json() if resp['nickName']: self.logger.info('Cookies 有效! ') nickname = resp['nickName'] self.logger.info('Hello, {}! '.format(nickname)) return True return False @staticmethod def input_time_random(): return random.randint(150, 201) def retry_if_exception(self, result): if isinstance(result, Exception): self.logger.error('Something Wring: {}'.format(result)) return result is Exception return result is None async def page_evaluate(self, page): await page.evaluate( '''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => undefined } }) }''') # 以下为插入中间js,将淘宝会为了检测浏览器而调用的js修改其结果。 await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {}, }; }''') await page.evaluate( '''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''') await page.evaluate( '''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''') async def mouse_slide(self, page=None): await asyncio.sleep(2) try: # 鼠标移动到滑块,按下,滑动到头(然后延时处理),松开按键 await page.hover('#yodaBox') # 不同场景的验证码模块可能名字不同。 await page.mouse.down() # 模拟按下鼠标 await page.mouse.move(2000, 0, {'delay': random.randint(1000, 2000)}) # js模拟拖动 await page.mouse.up() # 模拟松开鼠标 except Exception as e: return None, page else: await asyncio.sleep(2) slider_again = await page.Jeval('#yodaTip', 'node => node.textContent') # 判断是否通过 if slider_again != '验证码已发送,请稍后': return None, page else: # await page.screenshot({'path': './headless-slide-result.png'}) # 截图测试 return 1, page @staticmethod async def page_close(browser): """ 关闭浏览器驱动 :param browser: :return: """ for _page in await browser.pages(): await _page.close() await browser.close() async def login(self): # 以下使用await 可以针对耗时的操作进行挂起 # 记:一定要给pyppeteer权限删除用户数据, 即设置userDataDir: 文件夹名称, 否则会报错无法移除用户数据 browser = await launch( { 'headless': False, 'args': ['--no-sandbox', '--disable-infobars'], }, userDataDir=r'D:\login\userdata', args=['--window-size=1366, 768'] ) page = await browser.newPage() # 启动个新的浏览器页面 await page.setJavaScriptEnabled(enabled=True) # 启用js await page.setUserAgent( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299' ) # 设置模拟浏览器 self.logger.info('尝试登录...') await page.goto('https://passport.meituan.com/account/unitivelogin?service=www&continue=https%3A%2F%2Fwww.meituan.com%2Faccount%2Fsettoken%3Fcontinue%3Dhttp%253A%252F%252Fhf.meituan.com%252F') await self.page_evaluate(page) self.logger.info('输入账号密码...') await page.type('#login-email', self.username, {'delay': self.input_time_random() - 50}) time.sleep(1) await page.type('#login-password', self.password, {'delay': self.input_time_random()}) time.sleep(1) self.logger.info('点击提交...') await page.click('input.btn') time.sleep(2) await self.verify(page) @staticmethod async def get_position(captcha): """ 获取验证码位置元组 :return: """ location = captcha.location size = captcha.size top, bottom, left, right = location["y"], location["y"] + \ size["height"], location["x"], location["x"] + size["width"] return top, bottom, left, right, size async def verify(self, page): """ 风控认证 :param page: :return: """ self.logger.info('判断是否需要进行风控认证') await asyncio.sleep(3) if await page.xpath('//img[@id="yodaImgCode"]'): self.logger.info('账号存在风险, 需要进行风控认证! ') await page.screenshot({'path': './captcha_screenshot.png'}) # captcha = (await page.xpath('//img[@id="yodaImgCode"]'))[0] # top, bottom, left, right, size = await self.get_position(captcha) with open('captcha_screenshot.png', 'rb') as f: img_data = f.read() # img_data = screenshot.crop((left, top, right, bottom)) self.logger.info('使用超级鹰识别验证码...') ok, verify_code = image_to_text(img_data) if ok: self.logger.info('成功识别验证码! ') self.logger.info('输入验证码...') await page.type('#yodaImgCodeInput', verify_code, {'delay': self.input_time_random() - 50}) await asyncio.sleep(1) self.logger.info('点击提交...') await page.click('#yodaImgCodeSure') await asyncio.sleep(3) else: self.logger.info('账号正常, 未出现验证码! ') await asyncio.sleep(3) if page.url == 'http://hf.meituan.com/': self.logger.info('登录成功! ') cookies = await page.cookies() self.redis_client.save_cookies(self.site, self.username, cookies) return True elif 'verify.meituan.com' in page.url: self.logger.warning('为了您的账户安全,请先验证手机! ') await page.click('#yodaSmsCodeBtn') await asyncio.sleep(3) slider = page.Jeval('#yodaBox', 'node => node.style') if slider: self.logger.info('出现滑块情况判定') flag = await self.mouse_slide(page=page) if flag: self.logger.info('验证通过! ') else: self.logger.error('滑块验证失败! ') return False await asyncio.sleep(3) phone_vcode = input('请输入手机验证码 >> \n') await page.type('#yodaVerification', phone_vcode, {'delay': self.input_time_random() - 50}) await asyncio.sleep(1) self.logger.info('点击提交...') await page.click('#yodaSubmit') await asyncio.sleep(2) if await page.xpath('//div[@class="validate-info"]'): validate_info = await (await (await page.xpath('//div[@class="validate-info"]'))[0].getProperty('textContent')).jsonValue() self.logger.error(validate_info) return False elif page.url == 'http://hf.meituan.com/': self.logger.info('登录成功! ') cookies = await page.cookies() self.redis_client.save_cookies(self.site, self.username, cookies) return True elif await page.xpath('//img[@id="yodaImgCode"]'): self.logger.error('验证失败! ') return False @check_user() async def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return True self.logger.info('cookies 已过期! ') await self.login()
class OpenlawLogin: def __init__(self, username: str = None, password: str = None): self.site = 'openlaw' self.username = username self.password = password self.logger = get_logger() self.redis_client = RedisClient(self.logger) self.session = Session() self.session.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False def check_islogin(self, cookies): url = 'http://openlaw.cn/user/profile.jsp' res = self.session.get(url, cookies=cookies) if 'bbp-breadcrumb-root' in res.text: self.logger.info('Cookies 有效! ') bsobj = BeautifulSoup(res.text, 'lxml') nickname = bsobj.select('.bbp-breadcrumb-root')[0].get_text() self.logger.info('Hello, {}! '.format(nickname)) return True return False def _get_csrf(self): while True: base_url = 'http://openlaw.cn/login.jsp' res = self.session.get(base_url) cookies = res.cookies.get_dict() if cookies: soup = BeautifulSoup(res.text, 'lxml') _csrf = soup.find('input', {'name': '_csrf'})['value'] return _csrf, cookies time.sleep(1) def _encrypt_pwd(self): with open('encryptPwd.js', 'rb') as f: js = f.read().decode() ctx = execjs.compile(js) encrypt_pwd = ctx.call('encrypt_pwd', self.password) return encrypt_pwd @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): login_api = 'http://openlaw.cn/login' _csrf, cookies = self._get_csrf() _encrypt_pwd = self._encrypt_pwd() data = { 'username': self.username, '_csrf': _csrf, 'password': _encrypt_pwd, '_spring_security_remember_me': 'true' } res = self.session.post(login_api, data=data) bsobj = BeautifulSoup(res.text, 'lxml') if bsobj.select('.bbp-breadcrumb-root'): self.logger.info('登录成功!') nickname = bsobj.select('.bbp-breadcrumb-root')[0].get_text() self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return cookies elif '用户名或密码错误' in res.text: self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败! ') @check_user() def run(self, load_cookies: bool = True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login()
class XmlyFMLogin: def __init__(self, username: str = None, password: str = None): self.site = 'xmly' self.logger = get_logger() self.username = username self.password = password self.redis_client = RedisClient(self.logger) self.session = requests.session() self.session.headers = { 'Host': 'www.ximalaya.com', 'Referer': 'https://www.ximalaya.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36' } # 密码错误重置初始化 self.reset_flag = False with open('signature.js', 'rb') as f: js = f.read().decode() self.ctx = execjs.compile(js) @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def check_islogin(self, cookies): res = self.session.get('https://www.ximalaya.com/revision/main/getCurrentUser', cookies=cookies).json() if res['ret'] == 200: nickname = res['data']['nickname'] self.logger.info('登录成功!') self.logger.info('Hello, {}! '.format(nickname)) self.redis_client.save_cookies(self.site, self.username, cookies) return True return False def _get_token(self): url = 'https://www.ximalaya.com/passport/token/login' res = self.session.get(url).json() if res['ret'] == 0: return res['token'] return False def _get_signature(self, params): return self.ctx.call('get_signature', params) def _encrypt_pwd(self, token): return self.ctx.call('encrypwd', self.password, token) def _verify_account(self, token): """ 验证账号是否存在 :return: """ api = 'https://www.ximalaya.com/passport/login/checkAccount' data = { 'email': self.username, 'nonce': token, } params = '' for key, value in data.items(): params += key + '=' + value + '&' params += "e1996c7d6e0ff0664b28af93a2eeff8f8cae84b2402d158f7bb115b735a1663d" signature = self._get_signature(params) data.update({ 'signature': signature }) res = self.session.post(api, data=data).json() if res['success']: return True return False @loopUnlessSeccessOrMaxTry(3, sleep_time=3) def login(self): """ 模拟登录 :return: """ api = 'https://www.ximalaya.com/passport/v4/security/popupLogin' token = self._get_token() if token: if not self._verify_account(token): self.logger.error('账号不存在! ') return False # token 是一次性的 token = self._get_token() if token: encrypt_pwd = self._encrypt_pwd(token) data = { 'password': encrypt_pwd, 'rememberMe': 'true', 'account': self.username } res = self.session.post(api, data=data) cookies = res.cookies.get_dict() if self.check_islogin(cookies): return cookies if res.json()['errorMsg'] == '账号或密码不正确!': self.reset_flag = True raise Exception('账号或密码错误! ') raise Exception('登录失败: {} '.format(res.json()['errorMsg'])) @check_user() def run(self, load_cookies=True): if load_cookies: cookies = self.redis_client.load_cookies(self.site, self.username) if cookies: if self.check_islogin(cookies): return cookies self.logger.warning('Cookies 已过期') return self.login()