【自動化測試】:selenium破解 滑動驗證碼的問題


在測試中,系統登錄用到滑動驗證碼,根據系統驗證碼圖片的策略,分為有兩種定位模式;

 

 

左邊的圖是不帶缺口的,需要點擊拖動之后才有缺口模塊圖片出來;

右邊的是帶缺口的的背景圖,以及缺口滑塊的圖;

我們在自動化測試,拖動滑塊右移,主要難點就是確定缺口的橫坐標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()

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM