滑动验证码(无原图片处理)

 
 
from _pytest.mark import param
from seleniumbase.config import settings
from urllib3 import request
from pageobject.basepage import BasePage
import allure
from PIL import Image
from io import BytesIO
import re
import random
import base64
from tools import common
from selenium.webdriver import ActionChains
import cv2
from pageobject.page.navigation_bar import Navigation
import requests
import settings
import json
import time


class Login(BasePage):
    url = '/login'
    usernameInput = "//input[@name='username']"
    passwordInput = "//input[@name='password']"
    loginButton = "//input[@value='登录']/.."
    slider = "//div[@id='slider']"
    smallImageblocks = "//img[@class='smallImageblocks']"
    bgImage = "//img[@class='bgImage']"

    def open(self):
        '''
        打开账号密码登录页
        '''
        self._open(self.url)

    def type_name(self, value):
        '''
        输入用户名
        '''
        self.input(self.usernameInput, value)

    def type_pw(self, value):
        '''
        输入密码
        '''
        self.input(self.passwordInput, value)

    def click_login(self):
        '''
        点击登录
        '''
        self.click(self.loginButton)

    def hover_slider(self):
        '''
        停留在滑块上,并设置图片可见
        '''
        self.hover_on_element(self.slider)
        self.set_attribute(self.bgImage+"/..", 'class', 'slidePanel')

    def set_slider_success(self):
        '''
        设置图片验证成功
        '''
        self.set_attribute(self.slider+"/../..", 'class',
                           "sliderContainer sliderContainer_success")


class VerificationCode(Login):
    def get_img(self, loc):
        '''
        获取图片
        :param driver:
        :param div_class:
        :param num:
        :return:
        '''
        imge_url = ''
        location = {}
        imge_url = self.get_attribute(loc, 'src')
        try:
            location['x'] = re.findall(
                r'padding-top: (.*?)px;', self.get_attribute(loc, 'style'))[0]
        except Exception:
            location['x'] = 0
        try:
            location['y'] = re.findall(
                r'padding-top: (.*?)px; left: (.*?)px;', self.get_attribute(loc, 'style'))[1]
        except Exception:
            location['y'] = 0
        b64_data = imge_url.split(';base64,')[1]
        data = base64.b64decode(b64_data)
        img_content = BytesIO(data)
        image = Image.open(img_content)
        image = image.convert('RGB')
        name = f'{common.dir}/file/temporary/{str(random.randint(1, 100))}.jpg'
        image.save(name)
        return name

    def get_track(self, x):
        '''
        滑块移动轨迹
        初速度 v =0
        单位时间 t = 0.2
        位移轨迹 tracks = []
        当前位移 ccurrent = 0
        :param x:
        :return:
        '''
        # 移动轨迹, 即每次移动的距离,为一个列表,总和等于偏移量
        track = []
        # 当前位移, 也即记录当前移动了多少距离
        current = 0
        # 减速阈值, 也即开始减速的位置,这里设置为偏移量的9/10处开始减速,可以更改
        # mid = x * 4 / 5
        # 计算用的时间间隔
        t = 1
        # 初始速度
        v = 1

        while current < x:
            # if current < mid:
            #     # 当前位移小于4/5的偏移量时,加速度为2
            #     a = 1
            # else:
            #     # 当前位移大于4/5的偏移量时,加速度为-3
            #     a = -3
            # a = 0
            # # 初始速度v0
            # v0 = v
            # # 本次移动完成之后的速度v = v0 + at
            # v = v0 + a * t
            # # 本次移动距离x = v0t + 1/2 * a * t^2
            # move = v0 * t + 1 / 2 * a * t * t
            move = v * t
            # 当前位移, 这里都将move四舍五入取整
            current += round(move)
            # 将move的距离放入轨迹列表
            track.append(round(move))
            # print("轨迹列表:", track)
        return track

    def exchange(self, image):
        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        for row in range(0, image.shape[0]):
            for col in range(0, image.shape[1]):
                m = image[row][col]/255.00
                if m <= 0.2700:
                    image[row][col] = 0
                elif m > 0.2700 and m <= 0.5000:
                    image[row][col] = (m-0.27)/0.23*127
                elif m > 0.5000 and m <= 0.7200:
                    image[row][col] = (m-0.5)/0.22*255+(0.72-m)/0.22*127
                else:
                    image[row][col] = 255
        return image

    def identify_gap(self, bg, tp, out):
        '''
        获取缺口位置
        bg: 背景图片
        tp: 缺口图片
        out:输出图片
        '''
        # 读取背景图片和缺口图片
        bg_img = cv2.imread(bg)  # 背景图片
        tp_img = cv2.imread(tp)  # 缺口图片
        # time.sleep(1)
        # bg_img = self.exchange(bg_img)
        # tp_img = self.exchange(tp_img)

        # 识别图片边缘
        bg_edge = cv2.Canny(bg_img, 100, 200)
        tp_edge = cv2.Canny(tp_img, 100, 200)
        # 转换图片格式
        bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB)
        tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB)

        # 缺口匹配
        res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)  # 寻找最优匹配
        # 绘制方框
        th, tw = tp_pic.shape[:2]
        tl = max_loc  # 左上角点的坐标
        br = (tl[0]+tw, tl[1]+th)  # 右下角点的坐标
        cv2.rectangle(bg_img, tl, br, (0, 0, 255), 2)  # 绘制矩形
        cv2.imwrite(out, bg_img)  # 保存在本地
        # 返回缺口的X坐标
        # print(bg_img.shape[0])
        # if abs(bg_img.shape[0]-205) > 0:
        #     tl[0] += bg_img.shape[0]-205
        return tl[0]

    def move_slider(self,  track):
        """根据轨迹列表,拖动滑块到缺口处
        :param slider: 滑块
        :param track: 轨迹
        """
        ActionChains(self.driver.driver).click_and_hold(
            self.findelement(self.slider)).perform()
        for x in track:
            ActionChains(self.driver.driver).move_by_offset(
                xoffset=x, yoffset=0).perform()
        ActionChains(self.driver.driver).release().perform()


class Login_Action(VerificationCode):
    @ allure.step('输入用户名密码登录')
    def login(self, userName, userpw):
        '''
        输入用户名密码登录
        :param:userName:用户名
        :param:userpw:密码
        '''
        self.type_name(userName)
        self.type_pw(userpw)
        self.drag_slider()
        # self.quick_slider()
        self.click_login()
        assert Navigation(self.driver).get_username() == userName

    def drag_slider(self):
        '''
        第一种方法:移动滑块,完成验证
        '''
        self.hover_slider()
        name1 = self.get_img(self.bgImage)
        name2 = self.get_img(self.smallImageblocks)
        out = common.dir+"/file/temporary/out.jpg"
        x = self.identify_gap(
            name1, name2, out)
        print(x)
        if x > 200:
            x = x+20  # pass
        elif x > 150:
            x = x+16
        else:
            x = x+12  # pass
        tracks = self.get_track(x)
        self.move_slider(tracks)

    def quick_slider(self):
        '''
         第二种方法:通过接口获取验证成功
        '''
        response1 = requests.post(
            url=settings.Backstage_URL+"/share-auth/getImageVerifyCode")
        assert response1.status_code == 200
        content = json.loads(response1.text)
        for n in range(80, 230, 5):
            response = requests.get(url=settings.Backstage_URL + "/share-auth-center/validateUnlock",
                                    params={
                                        'checkMoveId': content['data']['checkMoveId'], 'xWidth': str(n)}
                                    )
            if response.text == 'true':
                print(n)
                break
            time.sleep(3)
        self.set_slider_success()
原文地址:https://www.cnblogs.com/ShadowXie/p/15028886.html