更新
多次测试,在计算像素误差时,改为40会更准确,因为有些图片会有淡阴影,导致获取距离时获取到淡阴影距离,而不是实际距离。
在需要验证时,会正常弹出如下验证码,已经其属性:
更改上图标红的属性,获取新图片:
新验证码和旧验证码有两处明显不同。
其一:可移动拼图
其二:需填充缺口
思路:
获取两个验证码的图片,比较两张验证码,获取像素不同的坐标距离x轴的距离,共两个值
两个值的差,就是拼图所要移动的距离。
模拟移动,测试数据即可。
import random
import time
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
def isSimilar(image1, image2, x, y):
"""
比较两种图片在(x,y)坐标点位置的像素值是否一样
:param image1:
:param image2:
:param x:
:param y:
:return: True->一样
"""
# 获取图片在x,y位置的像素值
pixel1 = image1.getpixel((x, y))
pixel2 = image2.getpixel((x, y))
# 计算要考虑误差的影响
if abs(pixel1[0] - pixel2[0]) >= 40 and abs(pixel1[1] - pixel2[1]) >= 40 and abs(pixel1[2] - pixel2[2]) >= 40:
return False
return True
def get_distance(image1, image2, x=None):
"""
通过对比两张一样的图片,获取不一样点的坐标
:param image1:
:param image2:
:param x
:return: i
"""
# 比较是比较每个坐标点的像素值
# 这两种图片是一样大小的
for i in range(x, 198): # x轴
for j in range(154): # y轴
# (i,j)--->图片上的每个坐标点
if isSimilar(image1, image2, i, j) == False:
return i
def get_tracks(distance):
# 拖动超过拼接位置8个距离
distance += 8
v = 0
t = 0.2
tracks = []
current = 0
mid = distance * 7 / 8
while current < distance:
if current < mid:
a = random.randint(2, 4)
else:
a = -random.randint(3, 5)
v0 = v
s = v0 * t + 0.5 * a * (t ** 2)
current += s
tracks.append(round(s))
v = v0 + a * t
# 补回超过距离为10,可多次测试,自测误差,进行多补或少补
x = [-2, -3, -3, -2]
tracks += x
return tracks
def login_verify(driver):
# 获取滑块
slider = driver.find_element_by_class_name('geetest_slider_button')
# 最大化浏览器
driver.maximize_window()
# 获取验证码所在位置
yz_element = driver.find_element_by_css_selector('[class="geetest_canvas_img geetest_absolute"]')
top = yz_element.location['y']
left = yz_element.location['x']
right = left + yz_element.size['width']
bottom = top + yz_element.size['height']
# 截图保存有缺口的图片完整图片
driver.save_screenshot('quan_quekou.png')
image_qk = Image.open('quan_quekou.png')
# 根据在上面获取的验证码位置在截图中截取验证码图片
image1 = image_qk.crop((left, top, right, bottom))
image1.save('imaeg1.png')
# 该canvas画布的属性:让其出现无缺的原图
driver.execute_script('document.getElementsByClassName("geetest_canvas_fullbg")[0].style="display: block"')
# 截图保存无缺口的图片完整图片
driver.save_screenshot('quan_wuque.png')
# 根据在上面获取的验证码位置在截图中截取验证码图片
image2 = Image.open('quan_wuque.png').crop((left, top, right, bottom))
image2.save('image2.png')
# 比较两张验证码的内容,获取距离
# x1:可移动拼图的左上角坐标距x轴的距离,
# 在获取距离方法中传0,
# 图片第一处不同是可移动拼图位置覆盖引起的图片像素不同,该不同在图片靠近x轴的地方
x1 = int(get_distance(image1, image2, 0))
# x2:图形缺口左上角坐标距x轴的距离,
# 在获取距离方法中传60,
# 图片第二处不同是在拼接处,稍远于x轴,在不同网站下,该值可能会有不同
x2 = int(get_distance(image1, image2, 60))
# 两距离的差即为拼图与缺口的真实距离
# print(x1, x2)
distance = abs(x1 - x2)
# # 3、使用鼠标动作链进行点击并悬浮
ActionChains(driver).click_and_hold(on_element=slider).perform()
# 获取移动距离列表
tracks = get_tracks(distance)
# 开始移动,加速,减速,超过,拖回
for x in tracks:
ActionChains(driver).move_by_offset(xoffset=x, yoffset=random.randint(0, 2)).perform()
time.sleep(1)
ActionChains(driver).release(slider).perform()
if __name__ == '__main__':
driver = webdriver.Chrome()
driver.get('https://account.cnblogs.com/signin')
# 隐式等待
driver.implicitly_wait(20)
# 输入用户名和密码
driver.find_element_by_xpath('//*[@id="mat-input-0"]').send_keys('账号')
driver.find_element_by_xpath('//*[@id="mat-input-1"]').send_keys('密码')
# 点击登录按钮
driver.find_element_by_xpath(
'//*[@class="mat-focus-indicator action-button ng-tns-c141-2 mat-flat-button mat-button-base mat-primary"]').click()
time.sleep(2)
login_verify(driver)