在测试中,系统登录用到滑动验证码,根据系统验证码图片的策略,分为有两种定位模式;
左边的图是不带缺口的,需要点击拖动之后才有缺口模块图片出来;
右边的是带缺口的的背景图,以及缺口滑块的图;
我们在自动化测试,拖动滑块右移,主要难点就是确定缺口的横坐标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()