在测试中,系统登录用到滑动验证码,根据系统验证码图片的策略,分为有两种定位模式;


左边的图是不带缺口的,需要点击拖动之后才有缺口模块图片出来;
右边的是带缺口的的背景图,以及缺口滑块的图;
我们在自动化测试,拖动滑块右移,主要难点就是确定缺口的横坐标X;
两种定位模式有啥区别呢?
主要体现在识别图片上缺口的位置上;
左边的识别方式是:保存无缺口的图1和有缺口的图2,对比两张图所有的RBG像素点,得到不一样的像素点,得到缺口的坐标位置;
右边的识别方式是:保存缺块图3和缺块背景图4,通过OpenCV提供了一个函数cv2.matchTemplate(),在较大背景图像4中搜索和查找模板图像3位置的方法。
我们系统用的是右边的方式,在鼠标放到滑动验证码拖动块上,图片显示出来,具体代码如下:
from PIL import Image
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
import cv2
import numpy as np
from io import BytesIO
import time
import requests
import os
class CrackSlider():
"""
通过浏览器截图,识别验证码中缺口位置,获取需要滑动距离,并模仿人类行为破解滑动验证码
"""
def __init__(self):
self.url = 'https://localhost/test/#/login'
self.driver = webdriver.Firefox()
self.wait = WebDriverWait(self.driver, 20)
self.zoom = 1
def open(self):
self.driver.get(self.url)
def get_pic(self):
time.sleep(2)
# 因为验证码模块需要鼠标位移上,才会显示,所以为了方便,通过js修改了显示属性,让元素可见
js = "document.getElementsByClassName('yidun_panel')[0].style.display='block';"
# 调用js脚本
self.driver.execute_script(js)
target = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'yidun_bg-img')))
template = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'yidun_jigsaw')))
target_link = target.get_attribute('src')
template_link = template.get_attribute('src')
target_img = Image.open(BytesIO(requests.get(target_link).content))
template_img = Image.open(BytesIO(requests.get(template_link).content))
target_img.save('target.jpg')
template_img.save('template.png')
local_img = Image.open('target.jpg')
size_loc = local_img.size
self.zoom = 320 / int(size_loc[0])
def crack_slider(self):
slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'yidun_slider')))
ActionChains(self.driver).click_and_hold(slider).perform()
for track in tracks['forward_tracks']:
ActionChains(self.driver).move_by_offset(xoffset=track, yoffset=0).perform()
time.sleep(0.5)
#for back_tracks in tracks['back_tracks']:
# ActionChains(self.driver).move_by_offset(xoffset=back_tracks, yoffset=0).perform()
ActionChains(self.driver).move_by_offset(xoffset=-4, yoffset=0).perform()
ActionChains(self.driver).move_by_offset(xoffset=4, yoffset=0).perform()
time.sleep(0.5)
ActionChains(self.driver).release().perform()
def get_tracks(self, distance):
print(distance)
distance += 20
v = 0
t = 0.2
forward_tracks = []
current = 0
mid = distance * 3 / 5 #减速阀值
while current < distance:
if current < mid:
a = 5 #加速度为+2
else:
a = -3 #加速度-3
s = v * t + 0.5 * a * (t ** 2)
v = v + a * t
current += s
forward_tracks.append(round(s))
back_tracks = [-3, -3, -2, -2, -2, -2, -2, -1, -1, -1]
return {'forward_tracks': forward_tracks, 'back_tracks': back_tracks}
def match(self, target, template):
img_rgb = cv2.imread(target)
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread(template, 0)
run = 1
w, h = template.shape[::-1]
print(w, h)
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
run = 1
# 使用二分法查找阈值的精确值
L = 0
R = 1
while run < 20:
run += 1
threshold = (R + L) / 2
print(threshold)
if threshold < 0:
print('Error')
return None
loc = np.where(res >= threshold)
print(len(loc[1]))
if len(loc[1]) > 1:
L += (R - L) / 2
print('目标区域起点x坐标为1:%d' % loc[1][0])
elif len(loc[1]) == 1:
print('目标区域起点x坐标为2:%d' % loc[1][0])
break
elif len(loc[1]) < 1:
#print('目标区域起点x坐标为3:%d' % loc[1][0])
R -= (R - L) / 2
return loc[1][0]
if __name__ == '__main__':
cs = CrackSlider()
cs.open()
target = 'target.jpg'
template = 'template.png'
cs.get_pic()
distance = cs.match(target, template)
tracks = cs.get_tracks((distance + 7) * cs.zoom) # 对位移的缩放计算
cs.crack_slider()
