Python3爬虫里关于识别微博宫格验证码的知识点详解

本节我们来介绍一下新浪微博宫格验证码的识别,此验证码是一种新型交互式验证码,每个宫格之间会有一条指示连线,指示了我们应该的滑动轨迹,我们需要按照滑动轨迹依次从起始宫格一直滑动到终止宫格才可以完成验证,如图所示:

我们可以访问新浪微博移动版登录页面就可以看到如上验证码,链接为:https://passport.weibo.cn/signin/login,当然也不是每次都会出现验证码,一般当频繁登录或者账号存在安全风险的时候会出现。

接下来我们就来试着识别一下此类验证码。

1. 本节目标

本节我们的目标是用程序来识别并通过微博宫格验证码的验证。

2. 准备工作

本次我们使用的 Python 库是 Selenium,使用的浏览器为 Chrome,在此之前请确保已经正确安装好了 Selenium 库、Chrome浏览器并配置好了 ChromeDriver,相关流程可以参考第一章的说明。

3. 识别思路

要识别首先要从探寻规律入手,那么首先我们找到的规律就是此验证码的四个宫格一定是有连线经过的,而且每一条连线上都会相应的指示箭头,连线的形状多样,如C型、Z型、X型等等,如图 8-26、8-27、8-28 所示:

Z 型

反向连线

在这里我们只需要挑选出不同的24张验证码图片并命名保存就好了,名称可以直接取作宫格的滑动的顺序,如某张验证码图片如图所示:

如上的 24 张图就是我们的模板,接下来我们在识别的时候只需要遍历模板进行匹配即可。

5. 模板匹配

上面的代码已经实现了将验证码保存下来的功能,通过调用 get_image() 方法我们便可以得到验证码图片对象,得到验证码对象之后我们就需要对其进行模板匹配了,定义如下的方法进行匹配:

from os import listdir
def detect_image(self, image):
    """
    匹配图片
    :param image: 图片
    :return: 拖动顺序
    """
    for template_name in listdir(TEMPLATES_FOLDER):
        print('正在匹配', template_name)
        template = Image.open(TEMPLATES_FOLDER + template_name)
        if self.same_image(image, template):
            # 返回顺序
            numbers = [int(number) for number in list(template_name.split('.')[0])]
            print('拖动顺序', numbers)
            return numbers

在这里 TEMPLATES_FOLDER 就是模板所在的文件夹,在这里我们用 listdir() 方法将所有模板的文件名称获取出来,然后对其进行遍历,通过 same_image() 方法对验证码和模板进行比对,如果成功匹配,那么就将匹配到的模板文件名转为列表,如匹配到了 3124.png,则返回结果 [3, 1, 2, 4]。

比对的方法实现如下:

def is_pixel_equal(self, image1, image2, x, y):
    """
    判断两个像素是否相同
    :param image1: 图片1
    :param image2: 图片2
    :param x: 位置x
    :param y: 位置y
    :return: 像素是否相同
    """
    # 取两个图片的像素点
    pixel1 = image1.load()[x, y]
    pixel2 = image2.load()[x, y]
    threshold = 20
    if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(
            pixel1[2] - pixel2[2]) < threshold:
        return True
    else:
        return False
def same_image(self, image, template):
    """
    识别相似验证码
    :param image: 待识别验证码
    :param template: 模板
    :return:
    """
    # 相似度阈值
    threshold = 0.99
    count = 0
    for x in range(image.width):
        for y in range(image.height):
            # 判断像素是否相同
            if self.is_pixel_equal(image, template, x, y):
                count += 1
    result = float(count) / (image.width * image.height)
    if result > threshold:
        print('成功匹配')
        return True
    return False

在这里比对图片也是利用了遍历像素的方法,same_image() 方法接收两个参数,image 为待检测的验证码图片对象,template 是模板对象,由于二者大小是完全一致的,所以在这里我们遍历了图片的所有像素点,比对二者同一位置的像素点是否相同,如果相同就计数加 1,最后计算一下相同的像素点占总像素的比例,如果该比例超过一定阈值那就判定为图片完全相同,匹配成功。在这里设定阈值为 0.99,即如果二者有 0.99 以上的相似比则代表匹配成功。

这样通过上面的方法,依次匹配 24 个模板,如果验证码图片正常,总能找到一个匹配的模板,这样最后就可以得到宫格的滑动顺序了。

6. 模拟拖动

得到了滑动顺序之后,我们接下来就是根据滑动顺序来拖动鼠标连接各个宫格了,方法实现如下:

def move(self, numbers):
    """
    根据顺序拖动
    :param numbers:
    :return:
    """
    # 获得四个按点
    circles = self.browser.find_elements_by_css_selector('.patt-wrap .patt-circ')
    dx = dy = 0
    for index in range(4):
        circle = circles[numbers[index] - 1]
        # 如果是第一次循环
        if index == 0:
            # 点击第一个按点
            ActionChains(self.browser) 
                .move_to_element_with_offset(circle, circle.size['width'] / 2, circle.size['height'] / 2) 
                .click_and_hold().perform()
        else:
            # 小幅移动次数
            times = 30
            # 拖动
            for i in range(times):
                ActionChains(self.browser).move_by_offset(dx / times, dy / times).perform()
                time.sleep(1 / times)
        # 如果是最后一次循环
        if index == 3:
            # 松开鼠标
            ActionChains(self.browser).release().perform()
        else:
            # 计算下一次偏移
            dx = circles[numbers[index + 1] - 1].location['x'] - circle.location['x']
            dy = circles[numbers[index + 1] - 1].location['y'] - circle.location['y']

在这里方法接收的参数就是宫格的点按顺序,如 [3, 1, 2, 4]。首先我们利用 find_elements_by_css_selector() 方法获取到四个宫格元素,是一个列表形式,每个元素代表一个宫格,接下来我们遍历了宫格的点按顺序,再做一系列对应操作。

其中如果是第一个宫格,那就直接鼠标点击并保持动作,否则移动到下一个宫格。如果是最后一个宫格,那就松开鼠标,否则计算移动到下一个宫格的偏移量。

通过四次循环,我们便可以成功操作浏览器完成宫格验证码的拖拽填充,松开鼠标之后即可识别成功。

运行效果如图所示:

44688c90c52ec03255e0028c83f7f74.png

鼠标会慢慢的从起始位置移动到终止位置,最后一个宫格松开之后便完成了验证码的识别。

至此,微博宫格验证码的识别就全部完成了。

识别完成之后验证码窗口会自动关闭,接下来直接点击登录按钮即可完成微博登录。

7. 本节代码

本节代码地址为:https://github.com/Python3WebSpider/CrackWeiboSlide。

8. 结语

本节我们介绍了一种常用的模板匹配识别图片的方式来识别验证码,并模拟了鼠标拖拽动作来实现验证码的识别。如果遇到类似的验证码,可以采用同样的思路进行识别。