滑动验证码(无原图片处理)
1 from _pytest.mark import param 2 from seleniumbase.config import settings 3 from urllib3 import request 4 from pageobject.basepage import BasePage 5 import allure 6 from PIL import Image 7 from io import BytesIO 8 import re 9 import random 10 import base64 11 from tools import common 12 from selenium.webdriver import ActionChains 13 import cv2 14 from pageobject.page.navigation_bar import Navigation 15 import requests 16 import settings 17 import json 18 import time 19 20 21 class Login(BasePage): 22 url = '/login' 23 usernameInput = "//input[@name='username']" 24 passwordInput = "//input[@name='password']" 25 loginButton = "//input[@value='登录']/.." 26 slider = "//div[@id='slider']" 27 smallImageblocks = "//img[@class='smallImageblocks']" 28 bgImage = "//img[@class='bgImage']" 29 30 def open(self): 31 ''' 32 打开账号密码登录页 33 ''' 34 self._open(self.url) 35 36 def type_name(self, value): 37 ''' 38 输入用户名 39 ''' 40 self.input(self.usernameInput, value) 41 42 def type_pw(self, value): 43 ''' 44 输入密码 45 ''' 46 self.input(self.passwordInput, value) 47 48 def click_login(self): 49 ''' 50 点击登录 51 ''' 52 self.click(self.loginButton) 53 54 def hover_slider(self): 55 ''' 56 停留在滑块上,并设置图片可见 57 ''' 58 self.hover_on_element(self.slider) 59 self.set_attribute(self.bgImage+"/..", 'class', 'slidePanel') 60 61 def set_slider_success(self): 62 ''' 63 设置图片验证成功 64 ''' 65 self.set_attribute(self.slider+"/../..", 'class', 66 "sliderContainer sliderContainer_success") 67 68 69 class VerificationCode(Login): 70 def get_img(self, loc): 71 ''' 72 获取图片 73 :param driver: 74 :param div_class: 75 :param num: 76 :return: 77 ''' 78 imge_url = '' 79 location = {} 80 imge_url = self.get_attribute(loc, 'src') 81 try: 82 location['x'] = re.findall( 83 r'padding-top: (.*?)px;', self.get_attribute(loc, 'style'))[0] 84 except Exception: 85 location['x'] = 0 86 try: 87 location['y'] = re.findall( 88 r'padding-top: (.*?)px; left: (.*?)px;', self.get_attribute(loc, 'style'))[1] 89 except Exception: 90 location['y'] = 0 91 b64_data = imge_url.split(';base64,')[1] 92 data = base64.b64decode(b64_data) 93 img_content = BytesIO(data) 94 image = Image.open(img_content) 95 image = image.convert('RGB') 96 name = f'{common.dir}/file/temporary/{str(random.randint(1, 100))}.jpg' 97 image.save(name) 98 return name 99 100 def get_track(self, x): 101 ''' 102 滑块移动轨迹 103 初速度 v =0 104 单位时间 t = 0.2 105 位移轨迹 tracks = [] 106 当前位移 ccurrent = 0 107 :param x: 108 :return: 109 ''' 110 # 移动轨迹, 即每次移动的距离,为一个列表,总和等于偏移量 111 track = [] 112 # 当前位移, 也即记录当前移动了多少距离 113 current = 0 114 # 减速阈值, 也即开始减速的位置,这里设置为偏移量的9/10处开始减速,可以更改 115 # mid = x * 4 / 5 116 # 计算用的时间间隔 117 t = 1 118 # 初始速度 119 v = 1 120 121 while current < x: 122 # if current < mid: 123 # # 当前位移小于4/5的偏移量时,加速度为2 124 # a = 1 125 # else: 126 # # 当前位移大于4/5的偏移量时,加速度为-3 127 # a = -3 128 # a = 0 129 # # 初始速度v0 130 # v0 = v 131 # # 本次移动完成之后的速度v = v0 + at 132 # v = v0 + a * t 133 # # 本次移动距离x = v0t + 1/2 * a * t^2 134 # move = v0 * t + 1 / 2 * a * t * t 135 move = v * t 136 # 当前位移, 这里都将move四舍五入取整 137 current += round(move) 138 # 将move的距离放入轨迹列表 139 track.append(round(move)) 140 # print("轨迹列表:", track) 141 return track 142 143 def exchange(self, image): 144 image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 145 for row in range(0, image.shape[0]): 146 for col in range(0, image.shape[1]): 147 m = image[row][col]/255.00 148 if m <= 0.2700: 149 image[row][col] = 0 150 elif m > 0.2700 and m <= 0.5000: 151 image[row][col] = (m-0.27)/0.23*127 152 elif m > 0.5000 and m <= 0.7200: 153 image[row][col] = (m-0.5)/0.22*255+(0.72-m)/0.22*127 154 else: 155 image[row][col] = 255 156 return image 157 158 def identify_gap(self, bg, tp, out): 159 ''' 160 获取缺口位置 161 bg: 背景图片 162 tp: 缺口图片 163 out:输出图片 164 ''' 165 # 读取背景图片和缺口图片 166 bg_img = cv2.imread(bg) # 背景图片 167 tp_img = cv2.imread(tp) # 缺口图片 168 # time.sleep(1) 169 # bg_img = self.exchange(bg_img) 170 # tp_img = self.exchange(tp_img) 171 172 # 识别图片边缘 173 bg_edge = cv2.Canny(bg_img, 100, 200) 174 tp_edge = cv2.Canny(tp_img, 100, 200) 175 # 转换图片格式 176 bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB) 177 tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB) 178 179 # 缺口匹配 180 res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED) 181 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配 182 # 绘制方框 183 th, tw = tp_pic.shape[:2] 184 tl = max_loc # 左上角点的坐标 185 br = (tl[0]+tw, tl[1]+th) # 右下角点的坐标 186 cv2.rectangle(bg_img, tl, br, (0, 0, 255), 2) # 绘制矩形 187 cv2.imwrite(out, bg_img) # 保存在本地 188 # 返回缺口的X坐标 189 # print(bg_img.shape[0]) 190 # if abs(bg_img.shape[0]-205) > 0: 191 # tl[0] += bg_img.shape[0]-205 192 return tl[0] 193 194 def move_slider(self, track): 195 """根据轨迹列表,拖动滑块到缺口处 196 :param slider: 滑块 197 :param track: 轨迹 198 """ 199 ActionChains(self.driver.driver).click_and_hold( 200 self.findelement(self.slider)).perform() 201 for x in track: 202 ActionChains(self.driver.driver).move_by_offset( 203 xoffset=x, yoffset=0).perform() 204 ActionChains(self.driver.driver).release().perform() 205 206 207 class Login_Action(VerificationCode): 208 @ allure.step('输入用户名密码登录') 209 def login(self, userName, userpw): 210 ''' 211 输入用户名密码登录 212 :param:userName:用户名 213 :param:userpw:密码 214 ''' 215 self.type_name(userName) 216 self.type_pw(userpw) 217 self.drag_slider() 218 # self.quick_slider() 219 self.click_login() 220 assert Navigation(self.driver).get_username() == userName 221 222 def drag_slider(self): 223 ''' 224 第一种方法:移动滑块,完成验证 225 ''' 226 self.hover_slider() 227 name1 = self.get_img(self.bgImage) 228 name2 = self.get_img(self.smallImageblocks) 229 out = common.dir+"/file/temporary/out.jpg" 230 x = self.identify_gap( 231 name1, name2, out) 232 print(x) 233 if x > 200: 234 x = x+20 # pass 235 elif x > 150: 236 x = x+16 237 else: 238 x = x+12 # pass 239 tracks = self.get_track(x) 240 self.move_slider(tracks) 241 242 def quick_slider(self): 243 ''' 244 第二种方法:通过接口获取验证成功 245 ''' 246 response1 = requests.post( 247 url=settings.Backstage_URL+"/share-auth/getImageVerifyCode") 248 assert response1.status_code == 200 249 content = json.loads(response1.text) 250 for n in range(80, 230, 5): 251 response = requests.get(url=settings.Backstage_URL + "/share-auth-center/validateUnlock", 252 params={ 253 'checkMoveId': content['data']['checkMoveId'], 'xWidth': str(n)} 254 ) 255 if response.text == 'true': 256 print(n) 257 break 258 time.sleep(3) 259 self.set_slider_success()