博客园滑块验证码破解

极验最初的滑块验证码是两张图,首先出现的是原图,点一下出现凹槽,然后拖动滑块进去,注意拖拽速度就可以破解成功。

原理: 分别遍历扫描原图和有凹槽的图片像素,进行对比,像素不一致的位置就是凹槽,拖动滑块到凹槽就可以破解。
  1. 模拟点击验证按钮
  2. 识别滑块缺口位置
  3. 拖动滑块到缺口位置
    现在极验登录升级了验证码,首先出来的就是凹槽,这样无法获取原图,就没有办法进行对比。登录地址为:https://auth.geetest.com/login/
    在这里插入图片描述
    极验现在登录不仅有这种滑块,还有图案顺序验证的方式在这里插入图片描述
    我们简化看一个博客园的例子,它采用的也是极验的验证码,登录地址为:
    https://account.cnblogs.com/signin
    在这里插入图片描述
    第一个点在于获取原图,如果获取到原图,就可以用上面的原理进行解决。在element调试分析如图一:
    在这里插入图片描述
    示例图二
    在这里插入图片描述
    对应的执行如下代码:
browser.execute_script(
            "var x=document.getElementsByClassName('geetest_canvas_fullbg geetest_fade geetest_absolute')[0];"
            "x.style.display='block';"
            "x.style.opacity=1;"
        )

图一与图二如果直接进行像素扫描,会由于小滑块的干扰,无法找到缺口,这也是一个点。示例图三:
在这里插入图片描述
执行如下代码去掉小滑块:

browser.execute_script(
            "document.getElementsByClassName('geetest_canvas_slice geetest_absolute')[0].remove();"
        )

然后就可以用利用上面原理的常规方法来解决了。
需要注意的是,在使用selenium的方法解决验证码图片的时候,截取的是window图,不能直接获取到验证码图片,要进行处理,获取到尺寸从屏幕截图当中抠出验证码图片。
在这里插入图片描述
如下:

    def get_captcha_pic(name="captcha.png"):
        """
        获取验证码图片
        :param name:
        :return: captcha图片对象
        """
        browser.save_screenshot(name)  # 截屏幕图
        im = Image.open(name)
        aa = (572, 193, 980, 452)  # 获取验证码图片在屏幕图当中的位置,测量不知道比例,可以再一边测好了,直接使用,否则返回的验证码图片不正确
        captcha = im.crop(aa)  # todo 识别的时候存在问题
        captcha.save(name)  # 保存验证码图片
        return captcha

在这里插入图片描述
在这里插入图片描述
两张图片进行对比扫描找缺口的时候,有一些小技巧,比如图三中的第一个箭头是阴影小滑块,要把干扰去掉

    def pixel_is_equal(image1, image2, x, y):
        """
        判断两张图片的像素是否相等,不相等即为缺口位置
        :param image1:
        :param image2:
        :param x:
        :param y:
        :return:
        """
        # 取两个图片的像素点
        pixel1 = image1.load()[x, y]
        pixel2 = image2.load()[x, y]
        threshold = 60  # 像素色差
        if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(
                pixel1[2] - pixel2[2]) < threshold:
            return True  # 像素色差小于60,默认为没区别
        else:
            return False

过程中还有一些其它问题,可以留言讨论,完整代码如下:

# -*- coding: utf-8 -*-
import time
from PIL import Image
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class BlogYuan(object):
    def __init__(self):
        self.browser = webdriver.Chrome()
        self.wait = WebDriverWait(self.browser, 20)

    def open(self):
        self.browser.get('https://account.cnblogs.com/signin')
        self.browser.implicitly_wait(3)
        input_username = self.browser.find_element_by_id('LoginName')
        input_username.send_keys('dawfawfaefag')
        input_password = self.browser.find_element_by_id('Password')
        input_password.send_keys('cawfafaf')
        submitBtn = self.browser.find_element_by_id('submitBtn')
        time.sleep(1)
        submitBtn.click()
        time.sleep(2)  # 等待验证码加载

    def get_captcha_pic(self, name="captcha.png"):
        """
        获取验证码图片
        :param name:
        :return: captcha图片对象
        """
        self.browser.save_screenshot(name)  # 截屏幕图
        im = Image.open(name)
        aa = (572, 193, 980, 452)  # 获取验证码图片在屏幕图当中的位置,测量不知道比例,可以再一边测好了
        captcha = im.crop(aa)  # todo 识别的时候存在问题
        captcha.save(name)  # 保存验证码图片
        return captcha

    def get_slider(self):
        """
        获取滑块
        :return:
        """
        slide = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
        return slide

    def pixel_is_equal(self, image1, image2, x, y):
        """
        判断两张图片的像素是否相等,不相等即为缺口位置
        :param image1:
        :param image2:
        :param x:
        :param y:
        :return:
        """
        # 取两个图片的像素点
        pixel1 = image1.load()[x, y]
        pixel2 = image2.load()[x, y]
        threshold = 60  # 像素色差
        if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(
                pixel1[2] - pixel2[2]) < threshold:
            return True  # 像素色差小于60,默认为没区别
        else:
            return False

    def get_gap(self, image1, image2):
        """
        获取缺口位置
        :param image1:完整图片
        :param image2: 带缺口的图片
        :return:
        """
        left = 60  # 设置一个起始量,因为验证码一般不可能在左边,加快识别速度
        for i in range(left, image1.size[0]):
            for j in range(image1.size[1]):
                if not self.pixel_is_equal(image1, image2, i, j):
                    left = i
                    return left
        return left

    def slide_path(self, gap):
        """
        滑动路径
        :param gap:
        :return: 滑动路径
        """
        # 移动轨迹
        track = []
        # 当前位移
        current = 0
        # 减速阈值
        mid = gap * 4 / 5
        # 计算间隔
        t = 0.2
        # 初速度
        v = 0

        while current < gap:
            if current < mid:
                # 加速度为正2
                a = 2
            else:
                # 加速度为负3
                a = -3
            # 初速度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
            # 当前位移
            current += move
            # 加入轨迹
            track.append(round(move))
        return track

    def move_to_gap(self, slider, track):
        """
        拖动滑块到缺口处
        :param slider: 滑块
        :param track: 轨迹
        :return:
        """
        ActionChains(self.browser).click_and_hold(slider).perform()
        for x in track:
            ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
        time.sleep(0.5)
        ActionChains(self.browser).release().perform()

    def check_gap(self, gap):
        """
        校准gap,可以自己调节,越精细,效果越好
        :param gap:
        :return: gap
        """
        aa = round(gap / 12.5)
        bb = {4: 38, 5: 41, 6: 42, 7: 43, 8: 46, 9: 52, 10: 54, 11: 59, 12: 62, 13: 65, 14: 68, 15: 71, 16: 74, 17: 79,
              18: 84, 19: 86, 20: 87, 21: 92, 22: 93, 23: 95, 24: 98, 25: 101}
        return gap - bb.get(int(aa))

    def run(self):
        self.open()
        #  移除滑块,否则滑块会对色差造成影响,无法获取gap
        self.browser.execute_script(
            "document.getElementsByClassName('geetest_canvas_slice geetest_absolute')[0].remove();"
        )
        image1 = self.get_captcha_pic("image1.png")  # 获取有缺口验证码图片,
        # 显示无缺口图片
        self.browser.execute_script(
            "var x=document.getElementsByClassName('geetest_canvas_fullbg geetest_fade geetest_absolute')[0];"
            "x.style.display='block';"
            "x.style.opacity=1;"
        )

        image2 = self.get_captcha_pic("image2.png")  # 获取无缺口验证码图片
        # 获取缺口的位置
        gap = self.get_gap(image1, image2)
        # 减去缺口位移
        gap -= 6
        # 获取滑动路径
        track = self.slide_path(self.check_gap(gap))
        # 拖动滑块
        slide = self.get_slider()
        self.move_to_gap(slide, track)
        time.sleep(1)
        self.browser.close()
        # 如果验证通过,执行,,,


if __name__ == "__main__":
    gt = BlogYuan()
    gt.run()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值