python易盾滑動驗證碼


上selenium 比較好上手的一種驗證碼, cv2模板匹配方法找缺口圖在背景圖中的位置, 計算要移動的距離, 移動缺口圖 ,要注意的是移動軌跡模擬人移動的加速和減速

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import time
from image_match import distance
from image_match import get_tracks
from image_match import getSlideInstance

class yiDundriver(object):
    def __init__(self, url, prt='', time2wait=10):
        ex_path = 'C:\Program Files\Google\Chrome\Application\chromedriver.exe'
        chrome_options = Options()
        # chrome_options.add_argument("--proxy-server=http://%s" % prt)
        chrome_options.add_argument('--disable-gpu') #谷歌文檔提到需要加上這個屬性來規避bug
        chrome_options.add_argument('disable-infobars')
        self.browser = webdriver.Chrome(executable_path= ex_path, chrome_options=chrome_options)
        self.browser.set_window_size(500,800)
        self.browser.implicitly_wait(10)
        self.browser.get(url)
        self.wait = WebDriverWait(self.browser, time2wait)

    def __clickVerifyBtn(self):
        verify_btn = self.wait.until(EC.element_to_be_clickable((By.ID, "btnCertificationpone")))
        verify_btn.click()

    def __slideVerifyCode(self):
        slider = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'yidun_slider')))
        ActionChains(self.browser).click_and_hold(slider).perform()
        slider_loc_x = slider.location["x"]
        img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "yidun_bg-img")))
        icon = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "yidun_jigsaw")))
        pic_width = img.size['width']
        icon_width = icon.size['width']
        img_tags = self.browser.find_elements_by_tag_name("img")
        img_url = img_tags[0].get_attribute("src")
        icon_url = img_tags[1].get_attribute("src")
        match_x = distance(img_url, icon_url, pic_width)
        if match_x == -1:
            raise Exception()

        slider_instance = getSlideInstance(pic_width, icon_width, match_x)
        tracks = get_tracks(slider_instance)

        for track in tracks:
            ActionChains(self.browser).move_by_offset(xoffset=track, yoffset=0).perform()
        else:
            ActionChains(self.browser).move_by_offset(xoffset=3, yoffset=0).perform()
            ActionChains(self.browser).move_by_offset(xoffset=-3, yoffset=0).perform()
            time.sleep(0.5)
            ActionChains(self.browser).release().perform()
        time.sleep(3)
        cur_loc_x = slider.location["x"]
        if cur_loc_x > slider_loc_x:
            print("success")
            return True
        else:
            return False

    def verifySlideCode(self,attempt_times=10):
        #嘗試attempt_times次滑動驗證,返回是否驗證通過
        self.wait.until(EC.text_to_be_present_in_element((By.CLASS_NAME,"yidun_tips__text"), r"向右拖動滑塊填充拼圖"))
        for attempt in range(attempt_times):
            try:
                if self.__slideVerifyCode():
                    return True
            except Exception as e:
                print(e)
                ActionChains(self.browser).release().perform()
                refresh = self.wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "yidun_refresh")))
                refresh.click()
                time.sleep(0.6)
        return False


if __name__ == '__main__':
    drv = yiDundriver('http://dun.163.com/trial/jigsaw-wap')
    drv.verifySlideCode()
image_match.py
import cv2
import numpy as np
import urllib.request as request
import time

def mathc_img(img_gray, template, value):
    #圖標和原圖的匹配位置,即為圖標要移動的距離
    w, h = template.shape[::-1]
    res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
    threshold = value
    loc = np.where(res >= threshold)
    result_size = len(loc[1])
    if result_size > 0:
        middle = round(result_size/2)
        '''
        #show match result
        guess_points = zip(*loc[::-1])
        for pt in guess_points:
            cv2.rectangle(img_gray, pt, (pt[0] + w, pt[1] + h), (7, 249, 151), 1)
        cv2.imshow('Detected', img_gray)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        '''
        return loc[1][middle]

    else:
        return -1

def cropHeight(icon):
    mid = round(icon.shape[1] / 2)
    c = icon[:, mid, 2]
    no0 = np.where(c != 0)
    first, last = no0[0][0], no0[0][-1]
    return first, last

def loadImg(url):
    resp = request.urlopen(url)
    image = np.asarray(bytearray(resp.read()), dtype="uint8")
    image = cv2.imdecode(image, cv2.IMREAD_COLOR)
    return image

def cropImage(img,top_y,bottom_y):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    crop_img = img_gray[top_y:bottom_y,:]
    return crop_img

def showImg(img,name):
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

def distance(img_url,icon_url,display_width):
    value = 0.45
    img_rgb = loadImg(img_url)
    tmp_rgb = loadImg(icon_url)
    crop_height = cropHeight(tmp_rgb)
    pic = cropImage(img_rgb,*crop_height)
    icon = cropImage(tmp_rgb,*crop_height)
    src_width = img_rgb.shape[1]
    guess_px = mathc_img(pic, icon, value)

    if guess_px is not -1:
        return round(guess_px * display_width / src_width)
    else:
        return -1

# copy demo
def get_tracks(distance):
    '''''
    拿到移動軌跡,模仿人的滑動行為,先勻加速后勻減速
    勻變速運動基本公式:
    ①v=v0+at
    ②s=v0t+½at²
    ③v²-v0²=2as

    :param distance: 需要移動的距離
    :return: 存放每0.3秒移動的距離
    '''
    # 初速度
    v = 0
    # 單位時間為0.2s來統計軌跡,軌跡即0.2內的位移
    t = 0.3
    # 位移/軌跡列表,列表內的一個元素代表0.2s的位移
    tracks = []
    # 當前的位移
    current = 0
    # 到達mid值開始減速
    mid = distance * 4 / 5

    while current < distance:
        if current < mid:
            # 加速度越小,單位時間的位移越小,模擬的軌跡就越多越詳細
            a = 2
        else:
            a = -3
            # 初速度
        v0 = v
        # 0.2秒時間內的位移
        s = v0 * t + 0.5 * a * (t ** 2)
        # 當前的位置
        current += s
        # 添加到軌跡列表
        tracks.append(round(s))

        # 速度已經達到v,該速度作為下次的初速度
        v = v0 + a * t
    return tracks

def getSlideInstance(img_w,icon_w,match_x):
    #考慮到滑塊和圖標的速度不總是1:1,獲取滑塊實際滑動的距離
    slider_width = 40
    iconMslider = icon_w - slider_width
    first_l = round(iconMslider / 2)
    mid_l = img_w - first_l
    #end_l = img_w - first_l - mid_l  #eliminate 1px error
    if match_x <= first_l:
        return match_x * 2
    elif match_x <= first_l + mid_l:
        return match_x + first_l
    else:
        return 2 * match_x - mid_l

 

 

 


免責聲明!

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



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