破解滑动验证码(selenium, opencv)

概述

由于本人近期参加一个游戏比赛,然后有个拉票的环节,票高者得人气奖。又比较懒不想到处拉票麻烦别人。就想自己尝试着破解验证码然后来达到刷票的目的。

这个也纯属娱乐,最后发现是不可行的。

最终目标:实现自动点击红星,并且拖动完成验证

破解滑动验证码(selenium, opencv)_第1张图片

自动点击并且获取图片

自动模拟的过程通过 selenium 实现。
selenium安装过程自己上网找,很简单,还需要安装一个webdriver

class WangYi(object):
    def __init__(self):
        self.browser = webdriver.Edge()
        self.back_img = None
        self.cut_img = None
        self.scaling_ratio = 1.0


    def visit(self, url):
        self.browser.get(url)
        WebDriverWait(self.browser, 10, 0.5).until(EC.element_to_be_clickable((By.CLASS_NAME, 'big-heart')))
        time.sleep(2)
        self.browser.find_element_by_class_name("big-heart").click() 

    def get_image(self):
        # 等待加载       
        WebDriverWait(self.browser, 10, 0.5).until(EC.visibility_of_element_located((By.CLASS_NAME, 'yidun_bgimg')))
        back_url= self.browser.find_element_by_class_name("yidun_bg-img").get_attribute('src')
        cut_url = self.browser.find_element_by_class_name("yidun_jigsaw").get_attribute('src')
        # 从url获取图片并保存到本地
        resq = requests.get(back_url)
        file = BytesIO(resq.content)
        back_img = Image.open(file)
        back_img.save("back_img.jpg")
        resq = requests.get(cut_url)
        file = BytesIO(resq.content)
        cut_img = Image.open(file)
        cut_img.save("cut_img.png")
        # opencv读取图片
        self.back_img = cv2.imread("back_img.jpg")
        self.cut_img = cv2.imread("cut_img.png")
        self.scaling_ratio = self.browser.find_element_by_class_name("yidun_bg-img").size['width'] / back_width
        return self.cut_img, self.back_img 

边缘检测计算滑动距离

计算滑动距离分为以下几步:

  • 利用opencv库中提供的边界查找函数(cv2.findContours)提取单片拼图边缘轨迹并构造成一个二维矩阵(算子)
  • 利用 高斯模糊算子( cv2.GaussianBlur )和 Canny 边缘检测算子( cv2.Canny )对背景图进行处理,凸显出拼图在图片中的边缘
  • 用拼图轨迹算子在处理后的背景图上进行 互相关操作,所得最大(小)值的位置就是拼图在背景图中的坐标

还需要注意的就是,在图像中计算出来的距离还需要根据图片与实际web中的比例进行scaling

def get_distance(self):
    back_canny = get_back_canny(self.back_img)
    operator = get_operator(self.cut_img)
    pos_x, max_value = best_match(back_canny, operator)
    distance = pos_x * self.scaling_ratio
    return distance

def read_img_file(cut_dir, back_dir):
    cut_image = cv2.imread(cut_dir)
    back_image = cv2.imread(back_dir)
    return cut_image, back_image

def best_match(back_canny, operator):
    max_value, pos_x = 0, 0
    for x in range(cut_width, back_width - cut_width):
        block = back_canny[:, x:x + cut_width]
        value = (block * operator).sum()
        if value > max_value:
            max_value = value
            pos_x = x
    return pos_x, max_value

def get_back_canny(back_img):
    img_blur = cv2.GaussianBlur(back_img, (3, 3), 0)
    img_gray = cv2.cvtColor(img_blur, cv2.COLOR_BGR2GRAY)
    img_canny = cv2.Canny(img_gray, 100, 200)
    return img_canny

def get_operator(cut_img):

    cut_gray = cv2.cvtColor(cut_img, cv2.COLOR_BGR2GRAY)

    _, cut_binary = cv2.threshold(cut_gray, 127, 255, cv2.THRESH_BINARY)
    # 获取边界
    _, contours, hierarchy = cv2.findContours(cut_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    # 获取最外层边界
    contour = contours[-1]
    # operator矩阵
    operator = np.zeros((cut_height, cut_width))
    # 根据 contour填写operator
    for point in contour:
        operator[point[0][1]][point[0][0]] = 1
    return operator

模拟人类滑动过程

其实这一步才是最难的,因为本人针对的是网易云盾的滑动验证码,滑动验证码会在拖拽完成之后,将交互数据发到云端。然后云端再通过机器学习反欺诈算法来检验这个交互过程是机器完成的还是人完成的。所以需要在代码中加入很多随机因子

def auto_drag(self, distance):
    element = self.browser.find_element_by_class_name("yidun_slider")

    # 这里就是根据移动进行调试,计算出来的位置不是百分百正确的,加上一点偏移
    #distance -= element.size.get('width') / 2
    distance += 13
    has_gone_dist = 0
    remaining_dist = distance
    #distance += randint(-10, 10)

    # 按下鼠标左键
    ActionChains(self.browser).click_and_hold(element).perform()
    time.sleep(0.5)
    while remaining_dist > 0:
        ratio = remaining_dist / distance
        if ratio < 0.2:
            # 开始阶段移动较慢
            span = random.randint(5, 8)
        elif ratio > 0.8:
            # 结束阶段移动较慢
            span = random.randint(5, 8)
        else:
            # 中间部分移动快
            span = random.randint(10, 16)
        ActionChains(self.browser).move_by_offset(span, random.randint(-5, 5)).perform()
        remaining_dist -= span
        has_gone_dist += span
        time.sleep(random.randint(5,20)/100)

    ActionChains(self.browser).move_by_offset(remaining_dist, random.randint(-5, 5)).perform()
    ActionChains(self.browser).release(on_element=element).perform()

总结

虽然最后没能真正成功完成破解(如果很轻松的话网易爸爸也不用混了),但是过程中还是学到了有关模拟还有CV的知识。
项目代码:https://github.com/ZezhongWang/BlogCode/tree/master/selenium-%20opencv
参考链接:
https://www.v2ex.com/t/371973
https://blog.csdn.net/hjxu2016/article/details/77833336/
https://blog.csdn.net/paololiu/article/details/52514504
https://blog.csdn.net/qq_22795513/article/details/53420482
https://www.itcodemonkey.com/article/5612.html

你可能感兴趣的:(计算机视觉)