一、环境介绍:
系统:window11 python:3.8 selenium:4.5
其他模块需要:requests、pillow
二、说明
总体思路:
1、利用selenium功能进行模拟鼠标、键盘自动化操作。
2、利用pillow功能进行图片的修改(主要是图片像素)
3、利用requests模块进行打码平台请求
4、利用第三方打码平台的API请求获取缺口的坐标x值,即缺口偏移量
注:此代码可以直接复制使用,代码仍有很大优化空间,本代码仅做技术研究使用,产生任何纠纷与本人无关。
本次只实现单次登录,并且即使拼图验证成功,账号本身有安全风险仍需要扫码登录。这个情况非本文探讨内容。
可以通过opencv和pillow模块实现本地识别缺口偏移量,但需要进行大量测试和准确率验证,节省时间本次采用第三方API进行识别,非广告,也可使用其他第三方API,效果一样。
未严格按照标准python工程代码格式,比如采用入口代码是:if __name__ == __main__
或者创建类的方式。
三、代码使用
使用须知
1、应该知道windows系统是否开启了缩放功能,滑块起始偏移量应根据具体的缩放倍数调整,不然可能出现滑块移动位置错误的问题。
2、需要获取背景图url,通过url下载的图片往往偏大,所以使用pillow的函数调整为图片元素属性设置的大小后再计算偏移量。
3、代码中用户名和密码需要自己修改。
4、部分函数方法不接受浮点数据,出现浮点数需要转换成int
5、找不到元素,注意切换iframe,操作完还需要切换回去。
6、需要自己手动下载浏览器驱动。selenium 4.9版本后好像会自动下载驱动
7、代码使用的是chorme浏览器,绕过检测代码不一定所有版本都有效
代码
代码里面有详细解释
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
import requests, json, base64, re, sys, time, os
from urllib.request import urlretrieve
from PIL import Image
def base64_api(img):
# 图鉴官方提供的接口模板,这里稍微修改了下
with open(img, 'rb') as f:
b64 = base64.b64encode(f.read()).decode()
data = {"username": "xxxx", "password": 'xxxxxx', "typeid": 33, "image": b64}
result = json.loads(requests.post("http://api.ttshitu.com/predict", json=data).text)
if result['success']:
print(result)
return result["data"]["result"]
else:
return result["message"]
return ""
#获取验证图片,并调整像素
def get_bg_offset(url):
src_png = r'.\download.png'
resize_png = r'.\resize.png'
#下载图片,和显示的图片大小不一样
urlretrieve(url, src_png)
# 打开原始图像
image = Image.open(src_png)
# 调整图像大小
'''#经过元素属性查看,在页面显示的图片应该是278x198.571,resize方法不接受浮点数,
Image.LANCZOS是改变图片大小的计算方式
'''
wide = 278
high = 198.571
resized_image = image.resize((int(wide), int(high)), Image.LANCZOS)
# 保存修改后的图像
resized_image.save(resize_png)
return resize_png
#移动滑块
def move_slider(move_x,driver):
'''
计算滑块实际偏移量
滑块每次起始大约离左边缘18.6个像素,这个是固定值,从元素属性中获知的。
由于系统开启了缩放,扩大了1.5倍,故约为28个像素
'''
slider_x = move_x - 28
print("鼠标移动量为:" + str(slider_x))
#定位滑块元素
slider_element = driver.find_element(By.XPATH,'//*[@id="tcOperation"]/div[6]')
# 创建ActionChains对象
actions = ActionChains(driver)
#防止移动太快,被检测出来是自动化操作,移动多次。计算分两次移动的量
temp = int(slider_x % 2)#取余,分两次移动可能还剩余1个像素
slider_x /= 2
# 按住并拖动元素,添加操作停顿时间,模拟人为操作,防止太快被判定为机器人操作
actions.click_and_hold(slider_element).move_by_offset(int(slider_x), 0).perform()
time.sleep(0.5)
actions.click_and_hold(slider_element).move_by_offset(-55, 0).perform()#左移55个像素
time.sleep(0.5)
actions.click_and_hold(slider_element).move_by_offset(55, 0).perform()#右移55个像素
time.sleep(0.5)
actions.click_and_hold(slider_element).move_by_offset(int(slider_x)+temp, 0).perform()
# 释放鼠标控制,这个是必须的,不然不进行最终验证
actions.release().perform()
# # 等待一段时间,可以根据需要调整等待时间
# driver.implicitly_wait(2)
return True
###--------------------------------------------------正式代码开始-----------------------------------------------------##
chrome_path = "./chromedriver.exe"
db_url = r"https://www.douban.com/"
driver = webdriver.Chrome(service=Service(chrome_path))
#绕过检测代码
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
#打开网址
driver.get(db_url)
# 将网页最大化
driver.maximize_window()
time.sleep(1)
# 先切换到 iframe
wait = WebDriverWait(driver, 10)
iframe = wait.until(EC.frame_to_be_available_and_switch_to_it((By.XPATH, '//*[@id="anony-reg-new"]/div/div[1]/iframe')))
# 定位并点击密码登录元素
password_login_element = wait.until(EC.element_to_be_clickable((By.CLASS_NAME, "account-tab-account")))
password_login_element.click()
time.sleep(0.5)
driver.find_element(By.XPATH,'//*[@id="username"]').send_keys("xxxxxx")
driver.find_element(By.XPATH,'//*[@id="password"]').send_keys("xxxxxxx")
time.sleep(1)
driver.find_element(By.XPATH,'/html/body/div[1]/div[2]/div[1]/div[5]/a').click()#点击后会弹出滑快验证窗口
#切换到验证的iframe
iframe = wait.until(EC.frame_to_be_available_and_switch_to_it((By.XPATH, '//*[@id="tcaptcha_iframe_dy"]')))
time.sleep(1.5)
# 定位验证码图片的元素
captcha_element = driver.find_element(By.XPATH,'//*[@id="slideBg"]')
# 获取验证码图片的URL
captcha_image_url = captcha_element.get_attribute("style")
print("提取到背景图片的style内容:" + captcha_image_url)
# 使用正则表达式匹配图片元素属性中的URL
url_pattern = r'url\("([^"]+)\*\*?"\)'
match = re.search(url_pattern, captcha_image_url)
if match:
# 提取括号里面的网址
url = match.group(1)
print("提取的网址:", url)
else:
print("未找到匹配的网址!")
sys.exit(1)
#下载、调整图片
resize_png = get_bg_offset(url)
if not os.path.exists(resize_png):
print(f"文件 '{resize_png}' 不存在!")
sys.exit(1)
else:
#通过打码平台得到偏移量,转换成数字
move_x = float(base64_api(resize_png))
print(f"背景图片空缺位置的起始x坐标为{move_x}")
#移动滑块move_x个像素
move_slider(move_x,driver)
# 切换回主文档
driver.switch_to.default_content()
#暂停程序的运行,控制台按任意键后退出程序,关闭浏览器
os.system("pause")
#退出浏览器
driver.quit()