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

 

 

 

posted @ 2019-06-01 13:41  1553  阅读(1297)  评论(0编辑  收藏  举报