diff --git a/cookiespool/config.py b/cookiespool/config.py index a07e817..09053f4 100644 --- a/cookiespool/config.py +++ b/cookiespool/config.py @@ -1,29 +1,14 @@ # Redis数据库地址 -REDIS_HOST = 'DataCrawl-Pool.redis.cache.chinacloudapi.cn' +REDIS_HOST = 'localhost' # Redis端口 REDIS_PORT = 6379 # Redis密码,如无填None -REDIS_PASSWORD = 'kk7YBCEHvswKYORLA6FCF3rfpi8mZXlKnAqBZIXqXXE=' +REDIS_PASSWORD = 'foobared' -# 配置信息,无需修改 -REDIS_DOMAIN = '*' -REDIS_NAME = '*' - -# 云打码相关配置到yundama.com申请注册 -YUNDAMA_USERNAME = 'Germey' -YUNDAMA_PASSWORD = 'CQCcqc123' -YUNDAMA_APP_ID = '3372' -YUNDAMA_APP_KEY = '1b586a30bfda5c7fa71c881075ba49d0' - -YUNDAMA_API_URL = 'http://api.yundama.com/api.php' - -# 云打码最大尝试次数 -YUNDAMA_MAX_RETRY = 20 - -# 产生器默认使用的浏览器 -DEFAULT_BROWSER = 'Chrome' +# 产生器使用的浏览器 +BROWSER_TYPE = 'Chrome' # 产生器类,如扩展其他站点,请在此配置 GENERATOR_MAP = { @@ -35,6 +20,10 @@ TESTER_MAP = { 'weibo': 'WeiboValidTester' } +TEST_URL_MAP = { + 'weibo': 'https://m.weibo.cn/api/container/getIndex?uid=1804544030&type=uid&page=1&containerid=1076031804544030' +} + # 产生器和验证器循环周期 CYCLE = 120 @@ -42,10 +31,9 @@ CYCLE = 120 API_HOST = '127.0.0.1' API_PORT = 5000 -# 进程开关 -# 产生器,模拟登录添加Cookies -GENERATOR_PROCESS = True -# 验证器,循环检测数据库中Cookies是否可用,不可用删除 +# 产生器开关,模拟登录添加Cookies +GENERATOR_PROCESS = False +# 验证器开关,循环检测数据库中Cookies是否可用,不可用删除 VALID_PROCESS = False # API接口服务 API_PROCESS = True diff --git a/cookiespool/db.py b/cookiespool/db.py index dfca05d..10a2bf7 100644 --- a/cookiespool/db.py +++ b/cookiespool/db.py @@ -1,209 +1,83 @@ import random - import redis - from cookiespool.config import * -from cookiespool.error import * class RedisClient(object): - def __init__(self, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD): + def __init__(self, type, website, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD): """ 初始化Redis连接 :param host: 地址 :param port: 端口 :param password: 密码 """ - if password: - self._db = redis.Redis(host=host, port=port, password=password) - else: - self._db = redis.Redis(host=host, port=port) - self.domain = REDIS_DOMAIN - self.name = REDIS_NAME + self.db = redis.Redis(host=host, port=port, password=password, decode_responses=True) + self.type = type + self.website = website - def _key(self, key): + def key(self): """ - 得到格式化的key - :param key: 最后一个参数key + 得到格式化的username + :param username: 最后一个参数username :return: """ - return "{domain}:{name}:{key}".format(domain=self.domain, name=self.name, key=key) + return "{type}:{website}".format(type=self.type, website=self.website) - def set(self, key, value): + def set(self, username, value): """ 设置键值对 - :param key: + :param username: :param value: :return: """ - raise NotImplementedError + return self.db.hset(self.key(), username, value) - def get(self, key): + def get(self, username): """ 根据键名获取键值 - :param key: + :param username: :return: """ - raise NotImplementedError + return self.db.hget(self.key(), username) - def delete(self, key): + def delete(self, username): """ 根据键名删除键值对 - :param key: - :return: - """ - raise NotImplementedError - - def keys(self): - """ - 得到所有的键名 - :return: - """ - return self._db.keys('{domain}:{name}:*'.format(domain=self.domain, name=self.name)) - - def flush(self): - """ - 清空数据库, 慎用 + :param username: :return: """ - self._db.flushall() - + return self.db.hdel(self.key(), username) -class CookiesRedisClient(RedisClient): - def __init__(self, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, domain='cookies', name='default'): + def count(self): """ - 管理Cookies的对象 - :param host: 地址 - :param port: 端口 - :param password: 密码 - :param domain: 域, 如cookies, account等 - :param name: 名称, 一般为站点名, 如 weibo, 默认 default + 获取数目 + :return: 数目 """ - RedisClient.__init__(self, host, port, password) - self.domain = domain - self.name = name - - def set(self, key, value): - try: - self._db.set(self._key(key), value) - except: - raise SetCookieError - - def get(self, key): - try: - return self._db.get(self._key(key)).decode('utf-8') - except: - return None - - def delete(self, key): - try: - print('Delete', key) - return self._db.delete(self._key(key)) - except: - raise DeleteCookieError + return len(self.db.hlen(self.key())) def random(self): """ - 随机得到一Cookies + 随机得到键值 :return: """ - try: - keys = self.keys() - return self._db.get(random.choice(keys)) - except: - raise GetRandomCookieError + return random.choice(self.db.hvals(self.key())) - def all(self): + def usernames(self): """ - 获取所有账户, 以字典形式返回 + 获取所有账户信息 :return: """ - try: - for key in self._db.keys('{domain}:{name}:*'.format(domain=self.domain, name=self.name)): - group = key.decode('utf-8').split(':') - if len(group) == 3: - username = group[2] - yield { - 'username': username, - 'cookies': self.get(username) - } - except Exception as e: - print(e.args) - raise GetAllCookieError - - def count(self): - """ - 获取当前Cookies数目 - :return: 数目 - """ - return len(self.keys()) - - - -class AccountRedisClient(RedisClient): - def __init__(self, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, domain='account', name='default'): - RedisClient.__init__(self, host, port, password) - self.domain = domain - self.name = name - - def set(self, key, value): - try: - return self._db.set(self._key(key), value) - except: - raise SetAccountError - - def get(self, key): - try: - return self._db.get(self._key(key)).decode('utf-8') - except: - raise GetAccountError + return self.db.hkeys(self.key()) def all(self): """ - 获取所有账户, 以字典形式返回 + 获取所有键值对 :return: """ - try: - for key in self._db.keys('{domain}:{name}:*'.format(domain=self.domain, name=self.name)): - group = key.decode('utf-8').split(':') - if len(group) == 3: - username = group[2] - yield { - 'username': username, - 'password': self.get(username) - } - except Exception as e: - print(e.args) - raise GetAllAccountError - - def delete(self, key): - """ - 通过用户名删除用户 - :param key: - :return: - """ - try: - return self._db.delete(self._key(key)) - except: - raise DeleteAccountError + return self.db.hgetall(self.key()) if __name__ == '__main__': - """ - conn = CookiesRedisClient() - conn.set('name', 'Mike') - conn.set('name2', 'Bob') - conn.set('name3', 'Amy') - print(conn.get('name')) - conn.delete('name') - print(conn.keys()) - print(conn.random()) - """ - # 测试 - conn = AccountRedisClient(name='weibo') - conn2 = AccountRedisClient(name='mweibo') - - - accounts = conn.all() - for account in accounts: - conn2.set(account['username'], account['password']) + conn = RedisClient('accounts', 'weibo') + result = conn.set('hell2o', 'sss3s') + print(result) diff --git a/cookiespool/error.py b/cookiespool/error.py deleted file mode 100644 index feed029..0000000 --- a/cookiespool/error.py +++ /dev/null @@ -1,48 +0,0 @@ -class CookiePoolError(Exception): - def __str__(self): - return repr('Cookie Pool Error') - - -class SetCookieError(CookiePoolError): - def __str__(self): - return repr('Set Cookie Error') - - -class GetCookieError(CookiePoolError): - def __str__(self): - return repr('Get Cookie Error') - - -class DeleteCookieError(CookiePoolError): - def __str__(self): - return repr('Delete Cookie Error') - - -class GetRandomCookieError(CookiePoolError): - def __str__(self): - return repr('Get Random Cookie Error') - - -class GetAllCookieError(CookiePoolError): - def __str__(self): - return repr('Get All Cookie Error') - - -class SetAccountError(CookiePoolError): - def __str__(self): - return repr('Set Account Error') - - -class DeleteAccountError(CookiePoolError): - def __str__(self): - return repr('Delete Account Error') - - -class GetAccountError(CookiePoolError): - def __str__(self): - return repr('Get Account Error') - - -class GetAllAccountError(CookiePoolError): - def __str__(self): - return repr('Get All Account Error') diff --git a/cookiespool/generator.py b/cookiespool/generator.py index c2356d5..ad7729f 100644 --- a/cookiespool/generator.py +++ b/cookiespool/generator.py @@ -1,83 +1,96 @@ import json - import requests -import time from selenium import webdriver from selenium.common.exceptions import WebDriverException, TimeoutException from selenium.webdriver import DesiredCapabilities from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait - from cookiespool.config import * -from cookiespool.db import CookiesRedisClient, AccountRedisClient -from cookiespool.verify import Yundama +from cookiespool.db import RedisClient +from login.weibo.cookies import WeiboCookies class CookiesGenerator(object): - def __init__(self, name='default', browser_type=DEFAULT_BROWSER): + def __init__(self, name='default'): """ 父类, 初始化一些对象 :param name: 名称 :param browser: 浏览器, 若不使用浏览器则可设置为 None """ self.name = name - self.cookies_db = CookiesRedisClient(name=self.name) - self.account_db = AccountRedisClient(name=self.name) - self.browser_type = browser_type - - def _init_browser(self, browser_type): + self.cookies_db = RedisClient('cookies', self.name) + self.accounts_db = RedisClient('accounts', self.name) + + def __del__(self): + self.close() + + def init_browser(self): """ 通过browser参数初始化全局浏览器供模拟登录使用 - :param browser: 浏览器 PhantomJS/ Chrome :return: """ - if browser_type == 'PhantomJS': + if BROWSER_TYPE == 'PhantomJS': caps = DesiredCapabilities.PHANTOMJS caps[ "phantomjs.page.settings.userAgent"] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36' self.browser = webdriver.PhantomJS(desired_capabilities=caps) self.browser.set_window_size(1400, 500) - elif browser_type == 'Chrome': + elif BROWSER_TYPE == 'Chrome': self.browser = webdriver.Chrome() - + def new_cookies(self, username, password): + """ + 新生成Cookies,子类需要重写 + :param username: 用户名 + :param password: 密码 + :return: + """ raise NotImplementedError - - def set_cookies(self, account): + + def process_cookies(self, cookies): """ - 根据账户设置新的Cookies - :param account: + 处理Cookies + :param cookies: :return: """ - results = self.new_cookies(account.get('username'), account.get('password')) - if results: - username, cookies = results - print('Saving Cookies to Redis', username, cookies) - self.cookies_db.set(username, cookies) - - + dict = {} + for cookie in cookies: + dict[cookie["name"]] = cookie["value"] + return dict + def run(self): """ 运行, 得到所有账户, 然后顺次模拟登录 :return: """ - accounts = self.account_db.all() - cookies = self.cookies_db.all() - # Account 中对应的用户 - accounts = list(accounts) - # Cookies中对应的用户 - valid_users = [cookie.get('username') for cookie in cookies] - print('Getting', len(accounts), 'accounts from Redis') - if len(accounts): - self._init_browser(browser_type=self.browser_type) - for account in accounts: - if not account.get('username') in valid_users: - print('Getting Cookies of ', self.name, account.get('username'), account.get('password')) - self.set_cookies(account) - print('Generator Run Finished') - + accounts_usernames = self.accounts_db.usernames() + cookies_usernames = self.cookies_db.usernames() + + for username in accounts_usernames: + if not username in cookies_usernames: + password = self.accounts_db.get(username) + print('正在生成Cookies', '账号', username, '密码', password) + result = self.new_cookies(username, password) + # 成功获取 + if result.get('status') == 1: + cookies = self.process_cookies(result.get('content')) + print('成功获取到Cookies', cookies) + if self.cookies_db.set(username, json.dumps(cookies)): + print('成功保存Cookies') + # 密码错误,移除账号 + elif result.get('status') == 2: + print(result.get('content')) + if self.accounts_db.delete(username): + print('成功删除账号') + else: + print(result.get('content')) + def close(self): + """ + 关闭 + :return: + """ try: print('Closing Browser') self.browser.close() @@ -87,111 +100,15 @@ class CookiesGenerator(object): class WeiboCookiesGenerator(CookiesGenerator): - def __init__(self, name='weibo', browser_type=DEFAULT_BROWSER): - """ - 初始化操作, 微博需要声明一个云打码引用 - :param name: 名称微博 - :param browser: 使用的浏览器 - """ - CookiesGenerator.__init__(self, name, browser_type) - self.name = name - self.ydm = Yundama(YUNDAMA_USERNAME, YUNDAMA_PASSWORD, YUNDAMA_APP_ID, YUNDAMA_APP_KEY) - - def _success(self, username): - wait = WebDriverWait(self.browser, 5) - success = wait.until(EC.visibility_of_element_located((By.CLASS_NAME, 'me_portrait_w'))) - if success: - print('登录成功') - self.browser.get('http://weibo.cn/') - - if "我的首页" in self.browser.title: - print(self.browser.get_cookies()) - cookies = {} - for cookie in self.browser.get_cookies(): - cookies[cookie["name"]] = cookie["value"] - print(cookies) - print('成功获取到Cookies') - return (username, json.dumps(cookies)) - - def new_cookies(self, username, password): - """ - 生成Cookies - :param username: 用户名 - :param password: 密码 - :return: 用户名和Cookies - """ - print('Generating Cookies of', username) - self.browser.delete_all_cookies() - self.browser.get('http://my.sina.com.cn/profile/unlogin') - wait = WebDriverWait(self.browser, 20) - - try: - login = wait.until(EC.visibility_of_element_located((By.ID, 'hd_login'))) - login.click() - user = wait.until( - EC.visibility_of_element_located((By.CSS_SELECTOR, '.loginformlist input[name="loginname"]'))) - user.send_keys(username) - psd = wait.until( - EC.visibility_of_element_located((By.CSS_SELECTOR, '.loginformlist input[name="password"]'))) - psd.send_keys(password) - submit = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.login_btn'))) - submit.click() - try: - result = self._success(username) - if result: - return result - except TimeoutException: - print('出现验证码,开始识别验证码') - yzm = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.loginform_yzm .yzm'))) - url = yzm.get_attribute('src') - cookies = self.browser.get_cookies() - cookies_dict = {} - for cookie in cookies: - cookies_dict[cookie.get('name')] = cookie.get('value') - response = requests.get(url, cookies=cookies_dict) - result = self.ydm.identify(stream=response.content) - if not result: - print('验证码识别失败, 跳过识别') - return - door = wait.until( - EC.visibility_of_element_located((By.CSS_SELECTOR, '.loginform_yzm input[name="door"]'))) - door.send_keys(result) - submit.click() - result = self._success(username) - if result: - return result - except WebDriverException as e: - print(e.args) - - -class MWeiboCookiesGenerator(CookiesGenerator): - def __init__(self, name='weibo', browser_type=DEFAULT_BROWSER): + def __init__(self, name='weibo'): """ 初始化操作, 微博需要声明一个云打码引用 :param name: 名称微博 :param browser: 使用的浏览器 """ - CookiesGenerator.__init__(self, name, browser_type) + CookiesGenerator.__init__(self, name) self.name = name - self.ydm = Yundama(YUNDAMA_USERNAME, YUNDAMA_PASSWORD, YUNDAMA_APP_ID, YUNDAMA_APP_KEY) - - def _success(self, username): - wait = WebDriverWait(self.browser, 5) - success = wait.until(EC.visibility_of_element_located((By.CLASS_NAME, 'me_portrait_w'))) - - if success: - print('登录成功') - self.browser.get('http://m.weibo.cn/') - - if "微博" in self.browser.title: - print(self.browser.get_cookies()) - cookies = {} - for cookie in self.browser.get_cookies(): - cookies[cookie["name"]] = cookie["value"] - print(cookies) - print('成功获取到Cookies') - return (username, json.dumps(cookies)) - + def new_cookies(self, username, password): """ 生成Cookies @@ -199,53 +116,10 @@ class MWeiboCookiesGenerator(CookiesGenerator): :param password: 密码 :return: 用户名和Cookies """ - print('Generating Cookies of', username) - self.browser.delete_all_cookies() - self.browser.get('http://my.sina.com.cn/profile/unlogin') - wait = WebDriverWait(self.browser, 20) - - try: - login = wait.until(EC.visibility_of_element_located((By.ID, 'hd_login'))) - login.click() - - user = wait.until( - EC.visibility_of_element_located((By.CSS_SELECTOR, '.loginformlist input[name="loginname"]'))) - user.send_keys(username) - psd = wait.until( - EC.visibility_of_element_located((By.CSS_SELECTOR, '.loginformlist input[name="password"]'))) - psd.send_keys(password) - submit = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.login_btn'))) - submit.click() - try: - result = self._success(username) - if result: - return result - except TimeoutException: - print('出现验证码,开始识别验证码') - yzm = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.loginform_yzm .yzm'))) - url = yzm.get_attribute('src') - cookies = self.browser.get_cookies() - - cookies_dict = {} - for cookie in cookies: - cookies_dict[cookie.get('name')] = cookie.get('value') - response = requests.get(url, cookies=cookies_dict) - result = self.ydm.identify(stream=response.content) - if not result: - print('验证码识别失败, 跳过识别') - return - door = wait.until( - EC.visibility_of_element_located((By.CSS_SELECTOR, '.loginform_yzm input[name="door"]'))) - door.send_keys(result) - submit.click() - result = self._success(username) - if result: - return result - except WebDriverException as e: - pass + return WeiboCookies(username, password, self.browser).main() if __name__ == '__main__': generator = WeiboCookiesGenerator() - generator._init_browser('Chrome') - generator.new_cookies('15197170054', 'gmwkms222') + generator.init_browser() + generator.run() diff --git a/cookiespool/importer.py b/cookiespool/importer.py index 519ba16..3400443 100644 --- a/cookiespool/importer.py +++ b/cookiespool/importer.py @@ -1,8 +1,8 @@ import requests -from cookiespool.db import AccountRedisClient +from cookiespool.db import RedisClient -conn = AccountRedisClient(name='weibo') +conn = RedisClient('accounts', 'weibo') def set(account, sep='----'): username, password = account.split(sep) diff --git a/cookiespool/tester.py b/cookiespool/tester.py index 6cf7ace..7078fd9 100644 --- a/cookiespool/tester.py +++ b/cookiespool/tester.py @@ -1,22 +1,21 @@ import json -from bs4 import BeautifulSoup import requests from requests.exceptions import ConnectionError from cookiespool.db import * -from cookiespool.generator import WeiboCookiesGenerator class ValidTester(object): def __init__(self, name='default'): self.name = name - self.cookies_db = CookiesRedisClient(name=self.name) - self.account_db = AccountRedisClient(name=self.name) - + self.cookies_db = RedisClient('cookies', self.name) + self.account_db = RedisClient('accounts', self.name) + def test(self, account, cookies): raise NotImplementedError - + def run(self): accounts = self.cookies_db.all() + print(accounts) for account in accounts: username = account.get('username') cookies = self.cookies_db.get(username) @@ -26,52 +25,18 @@ class ValidTester(object): class WeiboValidTester(ValidTester): def __init__(self, name='weibo'): ValidTester.__init__(self, name) - - def test(self, account, cookies): - print('Testing Account', account.get('username')) - try: - cookies = json.loads(cookies) - except TypeError: - # Cookie 格式不正确 - print('Invalid Cookies Value', account.get('username')) - self.cookies_db.delete(account.get('username')) - print('Deleted User', account.get('username')) - return None - try: - response = requests.get('http://weibo.cn', cookies=cookies) - if response.status_code == 200: - html = response.text - soup = BeautifulSoup(html, 'lxml') - title = soup.title.string - if title == '我的首页': - print('Valid Cookies', account.get('username')) - else: - print('Title is', title) - # Cookie已失效 - print('Invalid Cookies', account.get('username')) - self.cookies_db.delete(account.get('username')) - print('Deleted User', account.get('username')) - except ConnectionError as e: - print('Error', e.args) - print('Invalid Cookies', account.get('username')) - - -class MWeiboValidTester(ValidTester): - def __init__(self, name='weibo'): - ValidTester.__init__(self, name) - + def test(self, account, cookies): print('Testing Account', account.get('username')) try: cookies = json.loads(cookies) except TypeError: - # Cookie 格式不正确 print('Invalid Cookies Value', account.get('username')) self.cookies_db.delete(account.get('username')) print('Deleted User', account.get('username')) return None try: - test_url = 'http://m.weibo.cn/api/container/getIndex?uid=1804544030&type=uid&page=1&containerid=1076031804544030' + test_url = TEST_URL_MAP[self.name] response = requests.get(test_url, cookies=cookies, timeout=5, allow_redirects=False) if response.status_code == 200: print('Valid Cookies', account.get('username')) @@ -84,6 +49,3 @@ class MWeiboValidTester(ValidTester): print('Error', e.args) print('Invalid Cookies', account.get('username')) -if __name__ == '__main__': - tester = WeiboValidTester() - tester.run() diff --git a/cookiespool/verify.py b/cookiespool/verify.py deleted file mode 100644 index 5c3cc00..0000000 --- a/cookiespool/verify.py +++ /dev/null @@ -1,139 +0,0 @@ -import time - -import requests -from requests.exceptions import ConnectionError - -from cookiespool.config import * - - -class Yundama(): - def __init__(self, username, password, app_id, app_key, api_url=YUNDAMA_API_URL): - self.username = username - self.password = password - self.app_id = str(app_id) if not isinstance(app_id, str) else app_id - self.app_key = app_key - self.api_url = api_url - - def login(self): - """ - 登录云打码账户 - :return: - """ - try: - data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.app_id, - 'appkey': self.app_key} - response = requests.post(self.api_url, data=data) - if response.status_code == 200: - result = response.json() - print(result) - if 'ret' in result.keys() and result.get('ret') < 0: - return self.error(result.get('ret')) - else: - return result - return None - except ConnectionError: - return None - - def upload(self, files, timeout, code_type): - """ - 上传验证码得到识别结果 - :param files: - :param timeout: - :param code_type: - :return: - """ - try: - data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.app_id, - 'appkey': self.app_key, 'codetype': str(code_type), 'timeout': str(timeout)} - response = requests.post(self.api_url, data=data, files=files) - if response.status_code == 200: - return response.json() - return None - except ConnectionError: - return None - - def retry(self, cid, try_count=1): - """ - 临时识别不出, 传入cid重试 - :param cid: 验证码ID - :param try_count: 重试次数 - :return: 验证码结果 - """ - if try_count >= YUNDAMA_MAX_RETRY: - return None - print('Retrying: ', cid, 'Count: ', try_count) - time.sleep(2) - try: - data = {'method': 'result', 'cid': cid} - print(data) - response = requests.post(self.api_url, data=data) - if response.status_code == 200: - result = response.json() - print(result) - if 'ret' in result.keys() and result.get('ret') < 0: - print(self.error(result.get('ret'))) - if result.get('ret') == 0 and 'text' in result.keys(): - return result.get('text') - else: - return self.retry(cid, try_count + 1) - return None - except ConnectionError: - return None - - def identify(self, file=None, stream=None, timeout=60, code_type=5000): - """ - 主函数 - :param file: 文件名 - :param stream: 文件流, 优先于文件名 - :param timeout: 超时时间 - :param code_type: 验证码类型 - :return: 识别结果 - """ - if stream: - files = {'file': stream} - elif file: - files = {'file': open(file, 'rb')} - else: - return None - result = self.upload(files, timeout, code_type) - if 'ret' in result.keys() and result.get('ret') < 0: - print(self.error(result.get('ret'))) - if result.get('text'): - print('验证码识别成功', result.get('text')) - return result.get('text') - else: - return self.retry(result.get('cid')) - - def error(self, code): - """ - 报错原因 - :param code: 错误码 - :return: 错误原因 - """ - map = { - -1001: '密码错误', - -1002: '软件ID/密钥有误', - -1003: '用户被封', - -1004: 'IP被封', - -1005: '软件被封', - -1006: '登录IP与绑定的区域不匹配', - -1007: '账号余额为零', - -2001: '验证码类型有误', - -2002: '验证码图片太大', - -2003: '验证码图片损坏', - -2004: '上传验证码图片失败', - -3001: '验证码ID不存在 ', - -3002: '验证码还在识别', - -3003: '验证码识别超时', - -3004: '验证码看不清', - -3005: '验证码报错失败', - -4001: '充值卡号不正确或已使用', - -5001: '注册用户失败' - } - return '云打码' + map.get(code) - - -if __name__ == '__main__': - ydm = Yundama(YUNDAMA_USERNAME, YUNDAMA_PASSWORD, YUNDAMA_APP_ID, YUNDAMA_APP_KEY) - result = ydm.identify(file='getimage.jpg') - print(result) diff --git a/en.py b/en.py new file mode 100644 index 0000000..46215c1 --- /dev/null +++ b/en.py @@ -0,0 +1,6 @@ +from cookiespool.tester import WeiboValidTester + +if __name__ == '__main__': + tester = WeiboValidTester() + + tester.run() diff --git a/login/__init__.py b/login/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/login/weibo/__init__.py b/login/weibo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/login/weibo/cookies.py b/login/weibo/cookies.py new file mode 100644 index 0000000..12b8b78 --- /dev/null +++ b/login/weibo/cookies.py @@ -0,0 +1,231 @@ +import os +import time +from io import BytesIO +from PIL import Image +from selenium import webdriver +from selenium.common.exceptions import TimeoutException +from selenium.webdriver import ActionChains +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from os import listdir +from os.path import abspath, dirname + +TEMPLATES_FOLDER = dirname(abspath(__file__)) + '/templates/' + + +class WeiboCookies(): + def __init__(self, username, password, browser): + self.url = 'https://passport.weibo.cn/signin/login?entry=mweibo&r=https://m.weibo.cn/' + self.browser = browser + self.wait = WebDriverWait(self.browser, 20) + self.username = username + self.password = password + + def open(self): + """ + 打开网页输入用户名密码并点击 + :return: None + """ + self.browser.delete_all_cookies() + self.browser.get(self.url) + username = self.wait.until(EC.presence_of_element_located((By.ID, 'loginName'))) + password = self.wait.until(EC.presence_of_element_located((By.ID, 'loginPassword'))) + submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'loginAction'))) + username.send_keys(self.username) + password.send_keys(self.password) + time.sleep(1) + submit.click() + + def password_error(self): + """ + 判断是否密码错误 + :return: + """ + try: + return WebDriverWait(self.browser, 5).until( + EC.text_to_be_present_in_element((By.ID, 'errorMsg'), '用户名或密码错误')) + except TimeoutException: + return False + + def login_successfully(self): + """ + 判断是否登录成功 + :return: + """ + try: + return bool( + WebDriverWait(self.browser, 5).until(EC.presence_of_element_located((By.CLASS_NAME, 'drop-title')))) + except TimeoutException: + return False + + def get_position(self): + """ + 获取验证码位置 + :return: 验证码位置元组 + """ + try: + img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'patt-shadow'))) + except TimeoutException: + print('未出现验证码') + self.open() + time.sleep(2) + 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) + + def get_screenshot(self): + """ + 获取网页截图 + :return: 截图对象 + """ + screenshot = self.browser.get_screenshot_as_png() + screenshot = Image.open(BytesIO(screenshot)) + return screenshot + + def get_image(self, name='captcha.png'): + """ + 获取验证码图片 + :return: 图片对象 + """ + top, bottom, left, right = self.get_position() + print('验证码位置', top, bottom, left, right) + screenshot = self.get_screenshot() + captcha = screenshot.crop((left, top, right, bottom)) + return captcha + + def is_pixel_equal(self, image1, image2, x, y): + """ + 判断两个像素是否相同 + :param image1: 图片1 + :param image2: 图片2 + :param x: 位置x + :param y: 位置y + :return: 像素是否相同 + """ + # 取两个图片的像素点 + pixel1 = image1.load()[x, y] + pixel2 = image2.load()[x, y] + threshold = 20 + 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 same_image(self, image, template): + """ + 识别相似验证码 + :param image: 待识别验证码 + :param template: 模板 + :return: + """ + # 相似度阈值 + threshold = 0.99 + count = 0 + for x in range(image.width): + for y in range(image.height): + # 判断像素是否相同 + if self.is_pixel_equal(image, template, x, y): + count += 1 + result = float(count) / (image.width * image.height) + if result > threshold: + print('成功匹配') + return True + return False + + def detect_image(self, image): + """ + 匹配图片 + :param image: 图片 + :return: 拖动顺序 + """ + for template_name in listdir(TEMPLATES_FOLDER): + print('正在匹配', template_name) + template = Image.open(TEMPLATES_FOLDER + template_name) + if self.same_image(image, template): + # 返回顺序 + numbers = [int(number) for number in list(template_name.split('.')[0])] + print('拖动顺序', numbers) + return numbers + + def move(self, numbers): + """ + 根据顺序拖动 + :param numbers: + :return: + """ + # 获得四个按点 + circles = self.browser.find_elements_by_css_selector('.patt-wrap .patt-circ') + dx = dy = 0 + for index in range(4): + circle = circles[numbers[index] - 1] + # 如果是第一次循环 + if index == 0: + # 点击第一个按点 + ActionChains(self.browser) \ + .move_to_element_with_offset(circle, circle.size['width'] / 2, circle.size['height'] / 2) \ + .click_and_hold().perform() + else: + # 小幅移动次数 + times = 30 + # 拖动 + for i in range(times): + ActionChains(self.browser).move_by_offset(dx / times, dy / times).perform() + time.sleep(1 / times) + # 如果是最后一次循环 + if index == 3: + # 松开鼠标 + ActionChains(self.browser).release().perform() + else: + # 计算下一次偏移 + dx = circles[numbers[index + 1] - 1].location['x'] - circle.location['x'] + dy = circles[numbers[index + 1] - 1].location['y'] - circle.location['y'] + + def get_cookies(self): + """ + 获取Cookies + :return: + """ + return self.browser.get_cookies() + + def main(self): + """ + 破解入口 + :return: + """ + self.open() + if self.password_error(): + return { + 'status': 2, + 'content': '用户名或密码错误' + } + # 如果不需要验证码直接登录成功 + if self.login_successfully(): + cookies = self.get_cookies() + return { + 'status': 1, + 'content': cookies + } + # 获取验证码图片 + image = self.get_image('captcha.png') + numbers = self.detect_image(image) + self.move(numbers) + if self.login_successfully(): + cookies = self.get_cookies() + return { + 'status': 1, + 'content': cookies + } + else: + return { + 'status': 3, + 'content': '登录失败' + } + + +if __name__ == '__main__': + result = WeiboCookies('14773427930', 'x6pybpakq1').main() + print(result) diff --git a/login/weibo/templates/1234.png b/login/weibo/templates/1234.png new file mode 100644 index 0000000..ea74ec2 Binary files /dev/null and b/login/weibo/templates/1234.png differ diff --git a/login/weibo/templates/1243.png b/login/weibo/templates/1243.png new file mode 100644 index 0000000..7cb5973 Binary files /dev/null and b/login/weibo/templates/1243.png differ diff --git a/login/weibo/templates/1324.png b/login/weibo/templates/1324.png new file mode 100644 index 0000000..8fc1327 Binary files /dev/null and b/login/weibo/templates/1324.png differ diff --git a/login/weibo/templates/1342.png b/login/weibo/templates/1342.png new file mode 100644 index 0000000..47f0166 Binary files /dev/null and b/login/weibo/templates/1342.png differ diff --git a/login/weibo/templates/1423.png b/login/weibo/templates/1423.png new file mode 100644 index 0000000..5f749dd Binary files /dev/null and b/login/weibo/templates/1423.png differ diff --git a/login/weibo/templates/1432.png b/login/weibo/templates/1432.png new file mode 100644 index 0000000..6437b8c Binary files /dev/null and b/login/weibo/templates/1432.png differ diff --git a/login/weibo/templates/2134.png b/login/weibo/templates/2134.png new file mode 100644 index 0000000..7a1ab1f Binary files /dev/null and b/login/weibo/templates/2134.png differ diff --git a/login/weibo/templates/2143.png b/login/weibo/templates/2143.png new file mode 100644 index 0000000..78f630d Binary files /dev/null and b/login/weibo/templates/2143.png differ diff --git a/login/weibo/templates/2314.png b/login/weibo/templates/2314.png new file mode 100644 index 0000000..ecdd197 Binary files /dev/null and b/login/weibo/templates/2314.png differ diff --git a/login/weibo/templates/2341.png b/login/weibo/templates/2341.png new file mode 100644 index 0000000..715b555 Binary files /dev/null and b/login/weibo/templates/2341.png differ diff --git a/login/weibo/templates/2413.png b/login/weibo/templates/2413.png new file mode 100644 index 0000000..dc1d9f8 Binary files /dev/null and b/login/weibo/templates/2413.png differ diff --git a/login/weibo/templates/2431.png b/login/weibo/templates/2431.png new file mode 100644 index 0000000..4129c72 Binary files /dev/null and b/login/weibo/templates/2431.png differ diff --git a/login/weibo/templates/3124.png b/login/weibo/templates/3124.png new file mode 100644 index 0000000..aff3511 Binary files /dev/null and b/login/weibo/templates/3124.png differ diff --git a/login/weibo/templates/3142.png b/login/weibo/templates/3142.png new file mode 100644 index 0000000..7345ea6 Binary files /dev/null and b/login/weibo/templates/3142.png differ diff --git a/login/weibo/templates/3214.png b/login/weibo/templates/3214.png new file mode 100644 index 0000000..f9d6fd1 Binary files /dev/null and b/login/weibo/templates/3214.png differ diff --git a/login/weibo/templates/3241.png b/login/weibo/templates/3241.png new file mode 100644 index 0000000..cbb8697 Binary files /dev/null and b/login/weibo/templates/3241.png differ diff --git a/login/weibo/templates/3412.png b/login/weibo/templates/3412.png new file mode 100644 index 0000000..e53e363 Binary files /dev/null and b/login/weibo/templates/3412.png differ diff --git a/login/weibo/templates/3421.png b/login/weibo/templates/3421.png new file mode 100644 index 0000000..5a86030 Binary files /dev/null and b/login/weibo/templates/3421.png differ diff --git a/login/weibo/templates/4123.png b/login/weibo/templates/4123.png new file mode 100644 index 0000000..b18e0bb Binary files /dev/null and b/login/weibo/templates/4123.png differ diff --git a/login/weibo/templates/4132.png b/login/weibo/templates/4132.png new file mode 100644 index 0000000..b35f48b Binary files /dev/null and b/login/weibo/templates/4132.png differ diff --git a/login/weibo/templates/4213.png b/login/weibo/templates/4213.png new file mode 100644 index 0000000..db4e617 Binary files /dev/null and b/login/weibo/templates/4213.png differ diff --git a/login/weibo/templates/4231.png b/login/weibo/templates/4231.png new file mode 100644 index 0000000..70e54ac Binary files /dev/null and b/login/weibo/templates/4231.png differ diff --git a/login/weibo/templates/4312.png b/login/weibo/templates/4312.png new file mode 100644 index 0000000..5c69080 Binary files /dev/null and b/login/weibo/templates/4312.png differ diff --git a/login/weibo/templates/4321.png b/login/weibo/templates/4321.png new file mode 100644 index 0000000..a17c9d4 Binary files /dev/null and b/login/weibo/templates/4321.png differ