@ -1,209 +1,83 @@ |
|||||||
import random |
import random |
||||||
|
|
||||||
import redis |
import redis |
||||||
|
|
||||||
from cookiespool.config import * |
from cookiespool.config import * |
||||||
from cookiespool.error import * |
|
||||||
|
|
||||||
|
|
||||||
class RedisClient(object): |
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连接 |
初始化Redis连接 |
||||||
:param host: 地址 |
:param host: 地址 |
||||||
:param port: 端口 |
:param port: 端口 |
||||||
:param password: 密码 |
:param password: 密码 |
||||||
""" |
""" |
||||||
if password: |
self.db = redis.Redis(host=host, port=port, password=password, decode_responses=True) |
||||||
self._db = redis.Redis(host=host, port=port, password=password) |
self.type = type |
||||||
else: |
self.website = website |
||||||
self._db = redis.Redis(host=host, port=port) |
|
||||||
self.domain = REDIS_DOMAIN |
|
||||||
self.name = REDIS_NAME |
|
||||||
|
|
||||||
def _key(self, key): |
def key(self): |
||||||
""" |
""" |
||||||
得到格式化的key |
得到格式化的username |
||||||
:param key: 最后一个参数key |
:param username: 最后一个参数username |
||||||
:return: |
: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: |
:param value: |
||||||
:return: |
:return: |
||||||
""" |
""" |
||||||
raise NotImplementedError |
return self.db.hset(self.key(), username, value) |
||||||
|
|
||||||
def get(self, key): |
def get(self, username): |
||||||
""" |
""" |
||||||
根据键名获取键值 |
根据键名获取键值 |
||||||
:param key: |
:param username: |
||||||
:return: |
:return: |
||||||
""" |
""" |
||||||
raise NotImplementedError |
return self.db.hget(self.key(), username) |
||||||
|
|
||||||
def delete(self, key): |
def delete(self, username): |
||||||
""" |
""" |
||||||
根据键名删除键值对 |
根据键名删除键值对 |
||||||
:param key: |
:param username: |
||||||
:return: |
|
||||||
""" |
|
||||||
raise NotImplementedError |
|
||||||
|
|
||||||
def keys(self): |
|
||||||
""" |
|
||||||
得到所有的键名 |
|
||||||
:return: |
|
||||||
""" |
|
||||||
return self._db.keys('{domain}:{name}:*'.format(domain=self.domain, name=self.name)) |
|
||||||
|
|
||||||
def flush(self): |
|
||||||
""" |
|
||||||
清空数据库, 慎用 |
|
||||||
:return: |
:return: |
||||||
""" |
""" |
||||||
self._db.flushall() |
return self.db.hdel(self.key(), username) |
||||||
|
|
||||||
|
|
||||||
class CookiesRedisClient(RedisClient): |
def count(self): |
||||||
def __init__(self, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, domain='cookies', name='default'): |
|
||||||
""" |
""" |
||||||
管理Cookies的对象 |
获取数目 |
||||||
:param host: 地址 |
:return: 数目 |
||||||
:param port: 端口 |
|
||||||
:param password: 密码 |
|
||||||
:param domain: 域, 如cookies, account等 |
|
||||||
:param name: 名称, 一般为站点名, 如 weibo, 默认 default |
|
||||||
""" |
""" |
||||||
RedisClient.__init__(self, host, port, password) |
return len(self.db.hlen(self.key())) |
||||||
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 |
|
||||||
|
|
||||||
def random(self): |
def random(self): |
||||||
""" |
""" |
||||||
随机得到一Cookies |
随机得到键值 |
||||||
:return: |
:return: |
||||||
""" |
""" |
||||||
try: |
return random.choice(self.db.hvals(self.key())) |
||||||
keys = self.keys() |
|
||||||
return self._db.get(random.choice(keys)) |
|
||||||
except: |
|
||||||
raise GetRandomCookieError |
|
||||||
|
|
||||||
def all(self): |
def usernames(self): |
||||||
""" |
""" |
||||||
获取所有账户, 以字典形式返回 |
获取所有账户信息 |
||||||
:return: |
:return: |
||||||
""" |
""" |
||||||
try: |
return self.db.hkeys(self.key()) |
||||||
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 |
|
||||||
|
|
||||||
def all(self): |
def all(self): |
||||||
""" |
""" |
||||||
获取所有账户, 以字典形式返回 |
获取所有键值对 |
||||||
:return: |
:return: |
||||||
""" |
""" |
||||||
try: |
return self.db.hgetall(self.key()) |
||||||
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 |
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__': |
if __name__ == '__main__': |
||||||
""" |
conn = RedisClient('accounts', 'weibo') |
||||||
conn = CookiesRedisClient() |
result = conn.set('hell2o', 'sss3s') |
||||||
conn.set('name', 'Mike') |
print(result) |
||||||
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']) |
|
||||||
|
@ -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') |
|
@ -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) |
|
@ -0,0 +1,6 @@ |
|||||||
|
from cookiespool.tester import WeiboValidTester |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
tester = WeiboValidTester() |
||||||
|
|
||||||
|
tester.run() |
@ -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) |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 8.5 KiB |