滑动验证码最简单案例:破解qq空间(豆瓣网)滑块验证码(python+selenium实现)

破解qq空间(豆瓣网)滑块验证码

环境搭建

1、安装chrome浏览器和chromedriver

chromedriver下载连接:淘宝镜像
注意 :chromedriver的版本要与你使用的chrome版本对应
下载完成后将chromedriver.exe文件放到python.exe同一目录下。
在这里插入图片描述

2、搭建selenium环境

打开cmd,用命令直接安装

pip install selenium

然后在pycharm中的file–>settings–>project interpreter 中点击**+**号引入selenium包。
在这里插入图片描述
如果引入包后仍然报错,可以参考:关于pip安装第三方库,但PyCharm中却无法识别的问题

3、环境测试

from selenium import webdriver  # 导入webdriver包
driver = webdriver.Chrome()  # 初始化一个火狐浏览器实例:driver
driver.maximize_window()  # 最大化浏览器
driver.get("https://www.baidu.com")  # 通过get()方法,打开一个url站点

如果成功打开百度,说明环境已经搭好!

滑块验证码破解思路

计算滑块需要滑动的距离

1、方法一:对比缺口图片和原图的像素值
这个方法的关键在于如何获取到原图。一般情况下我们打开浏览器的开发者模式,可以直接定位到验证码图片的位置。根据图片的呈现方式,我大概分为两种情况:一种是使用img标签呈现,这种方式一般会把图片的路径(qq空间、豆瓣网等)或者经过加密后(京东)放入到标签中,而另一种是使用canvas呈现,这种方式就需要通过修改canvas中的display属性来呈现单独的图片,但是我们无法直接通过路径去获取图片,只能通过截图的方式。(例如:极验的滑块验证码)
2、方法二:使用opencv方法处理缺口图片
就是将缺口图片处理成黑白图片,然后再计算缺口的距离。具体请参考opencv破解滑块验证码

滑块按照怎样的轨迹滑动

1、直接匀速滑动(适用没有使用机器学习验证的网站:qq空间、豆瓣网等)
2、模拟人的动作按照先匀加速,后匀减速滑动,最后多滑一段距离再退回来。(适用于使用机器学习验证的网站:极验、京东等)具体请问度娘。

实现

 本次要讲的是方法一破解qq空间验证码,也是最简单的方法。(豆瓣网的原理一模一样,甚至连验证码图片都是一样的,不难猜测应该是使用了同样的验证码接口)方法一的关键在于我们怎么得到验证码的原图,我们把这个问题解决了其他的都好办了!
 首先我们进入qq空间登录界面,随便输入一个qq号和密码点击登录按钮就会弹出滑块验证码,只能是电脑没有记录的才行。然后跳转到控制台–>element,我们可以很容易找到缺口图片和滑块图片,但是怎么也无法找到完整图片,很头疼吧!
在这里插入图片描述
 别放弃,我们继续在控制台中找到network,再选择img,我们又发现了之前的两张图片,我们双击图片把图片路径复制下来分析一下。在这里插入图片描述
发现缺口图片路径:index=1&image=*********&img_index=1&subsid=3
滑块图片路径:index=2&image=*********&img_index=2&subsid=3
于是尝试修改两个index等于0,再访问图片路径,奇迹出现了,我们得到原图的路径,这样问题不就解决了吗?
在这里插入图片描述
 获取缺口图片和原图代码实现如下:

    def get_img(self):
        """
        获取验证码阴影图和原图
        :return:
        """
        self.driver.switch_to.frame('tcaptcha_iframe')
        time.sleep(3)
        # 获取有阴影的图片
        src = self.driver.find_element_by_id('slideBg').get_attribute('src')
        # 分析图片地址,发现原图地址可以通过阴影图地址改动获取 只需要修改一下图片路径中的index
        #print(src)
        src_bg=src.replace('index=1','index=0')
        src_bg=src_bg.replace('img_index=1','img_index=0')
        #print(src_bg)
        # 将图片下载到本地
        urlretrieve(src, 'img1.png')
        urlretrieve(src_bg, 'img2.png')
        #读取本地图片
        captcha1 = Image.open('img1.png')
        captcha2 = Image.open('img2.png')
        return captcha1, captcha2

 得到两张图片之后,我们是不是就可以直接进行比较计算出缺口的距离的呢?按道理没错,但是这里有一个比较坑的地方就是我们下载下来的图片比网站中的图片要大,所以我们必须将图片缩小之后再进行计算。具体代码如下:

    def resize_img(self, img):
        """
        下载的图片把网页中的图片进行了放大,所以将图片还原成原尺寸
        :param img: 图片
        :return: 返回还原后的图片
        """
        # 通过本地图片与原网页图片的比较,计算出的缩放比例 原图(680x390)缩小图(280x161)
        a = 2.428
        (x, y) = img.size
        x_resize = int(x // a)
        y_resize = int(y // a)
        """
        Image.NEAREST :低质量
        Image.BILINEAR:双线性
        Image.BICUBIC :三次样条插值
        Image.ANTIALIAS:高质量
        """
        img = img.resize((x_resize, y_resize), Image.ANTIALIAS)
        return img

    def is_pixel_equal(self, img1, img2, x, y):
        """
        比较两张图片同一点上的像数值,差距大于设置标准返回False
        :param img1: 阴影图
        :param img2: 原图
        :param x: 横坐标
        :param y: 纵坐标
        :return: 是否相等
        """
        pixel1, pixel2 = img1.load()[x, y], img2.load()[x, y]
        sub_index = 100
        #比较RGB各分量的值
        if abs(pixel1[0] - pixel2[0]) < sub_index and abs(pixel1[1] - pixel2[1]) < sub_index and abs(
                pixel1[2] - pixel2[2]) < sub_index:
            return True
        else:
            return False

    def get_gap_offset(self, img1, img2):
        """
        获取缺口的偏移量
        """
        distance = 70
        for i in range(distance, img1.size[0]):
            for j in range(img1.size[1]):
                # 两张图片对比,(i,j)像素点的RGB差距,过大则该x为偏移值
                if not self.is_pixel_equal(img1, img2, i, j):
                    distance = i
                    return distance
        return distance

 滑块的滑动轨迹其实设计匀速运动就可以了,也可以参考代码中的轨迹设计。(是不是非常简单!!!诚不欺您!)

下面是全部实现代码:

import time
import random
from selenium import webdriver
from urllib.request import urlretrieve
from PIL import Image
from selenium.webdriver.common.action_chains import ActionChains


class Tencent():
    def __init__(self):
        """
        初始化属性,传入url地址,驱动路径,浏览器窗口最大化,伪造ua
        """
        self.url = 'https://qzone.qq.com/'
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'}

    def input_username_password(self, account, password):
        """
        打开浏览器,传入账号、密码,定位到登录窗口,切换登陆方式
        :param account:
        :param password:
        :return:
        """
        self.driver.get(self.url)
        time.sleep(1)
        self.driver.switch_to.frame('login_frame')
        self.driver.find_element_by_id('switcher_plogin').click()
        time.sleep(1)
        self.driver.find_element_by_id('u').send_keys(account)
        time.sleep(0.5)
        self.driver.find_element_by_id('p').send_keys(password)
        time.sleep(0.5)
        self.driver.find_element_by_class_name('login_button').click()

    def get_img(self):
        """
        获取验证码阴影图和原图
        :return:
        """
        self.driver.switch_to.frame('tcaptcha_iframe')
        time.sleep(3)
        # 获取有阴影的图片
        src = self.driver.find_element_by_id('slideBg').get_attribute('src')
        # 分析图片地址,发现原图地址可以通过阴影图地址改动获取 只需要修改一下图片路径中的index
        #print(src)
        src_bg=src.replace('index=1','index=0')
        src_bg=src_bg.replace('img_index=1','img_index=0')
        #print(src_bg)
        # 将图片下载到本地
        urlretrieve(src, 'img1.png')
        urlretrieve(src_bg, 'img2.png')
        #读取本地图片
        captcha1 = Image.open('img1.png')
        captcha2 = Image.open('img2.png')
        return captcha1, captcha2

    def resize_img(self, img):
        """
        下载的图片把网页中的图片进行了放大,所以将图片还原成原尺寸
        :param img: 图片
        :return: 返回还原后的图片
        """
        # 通过本地图片与原网页图片的比较,计算出的缩放比例 原图(680x390)缩小图(280x161)
        a = 2.428
        (x, y) = img.size
        x_resize = int(x // a)
        y_resize = int(y // a)
        """
        Image.NEAREST :低质量
        Image.BILINEAR:双线性
        Image.BICUBIC :三次样条插值
        Image.ANTIALIAS:高质量
        """
        img = img.resize((x_resize, y_resize), Image.ANTIALIAS)
        return img

    def is_pixel_equal(self, img1, img2, x, y):
        """
        比较两张图片同一点上的像数值,差距大于设置标准返回False
        :param img1: 阴影图
        :param img2: 原图
        :param x: 横坐标
        :param y: 纵坐标
        :return: 是否相等
        """
        pixel1, pixel2 = img1.load()[x, y], img2.load()[x, y]
        sub_index = 100
        #比较RGB各分量的值
        if abs(pixel1[0] - pixel2[0]) < sub_index and abs(pixel1[1] - pixel2[1]) < sub_index and abs(
                pixel1[2] - pixel2[2]) < sub_index:
            return True
        else:
            return False

    def get_gap_offset(self, img1, img2):
        """
        获取缺口的偏移量
        """
        distance = 70
        for i in range(distance, img1.size[0]):
            for j in range(img1.size[1]):
                # 两张图片对比,(i,j)像素点的RGB差距,过大则该x为偏移值
                if not self.is_pixel_equal(img1, img2, i, j):
                    distance = i
                    return distance
        return distance

    def get_track(self, distance):
        """
        计算滑块的移动轨迹
        """
        # 通过观察发现滑块并不是从0开始移动,有一个初始值
        distance -= 30
        a = distance / 4
        track = [a, a, a, a]
        return track

    def shake_mouse(self):
        """
        模拟人手释放鼠标抖动
        """
        ActionChains(self.driver).move_by_offset(xoffset=-2, yoffset=0).perform()
        ActionChains(self.driver).move_by_offset(xoffset=2, yoffset=0).perform()

    def operate_slider(self, track):
        """
        拖动滑块
        当你调用ActionChains的方法时,不会立即执行,而是会将所有的操作按顺序存放在一个队列里,当你调用perform()方法时,队列中的时间会依次执行。
        :param track: 运动轨迹
        :return:
        """
        # 定位到拖动按钮
        slider_bt = self.driver.find_element_by_xpath('//div[@class="tc-drag-thumb"]')
        # 点击拖动按钮不放
        ActionChains(self.driver).click_and_hold(slider_bt).perform()
        # 按正向轨迹移动
        # move_by_offset函数是会延续上一步的结束的地方开始移动
        for i in track:
            ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
            print(i)
            time.sleep(random.random() / 100)  # 每移动一次随机停顿0-1/100秒之间骗过了极验,通过率很高
        time.sleep(random.random())
        # 按逆向轨迹移动
        back_tracks = [-1, -0.5, -1]
        for i in back_tracks:
            time.sleep(random.random() / 100)
            ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
        # 模拟人手抖动
        self.shake_mouse()
        time.sleep(random.random())
        # 松开滑块按钮
        ActionChains(self.driver).release().perform()

    def login(self, account, password):
        """
        实现主要的登陆逻辑
        :param account:账号
        :param password: 密码
        :return:
        """
        self.input_username_password(account, password)
        time.sleep(2)
        a, b = self.get_img()
        a = self.resize_img(a)
        b = self.resize_img(b)
        distance = self.get_gap_offset(a, b)
        track = self.get_track(distance)
        self.operate_slider(track)

if __name__ == '__main__':
    qq = Tencent()
    account = '2178452154'
    password = 'B20170304430ZZH'
    qq.login(account, password)

其实还有个更简单的方法破解qq空间和豆瓣网这个滑块验证码,就是直接量出滑块到缺口的距离,直接固定这个值,让滑块每次都滑动这个距离。(我一个同班同学用这个方法破解了豆瓣网的滑块验证码,确信!)其实我们多刷新几次可以发现缺口的位置其实是在不断变化的,这也说明了这个验证码的设计确实太简单了!(兄弟们,把离谱打在公平上!!!)

结尾

 事情的起因是这个学期的网络安全期末大作业,刚刚开始想做一个人脸识别的注册和登录调用百度的人脸识别api接口整合到我上个学期做的vue+springboot的项目里面,写了三天前端都搭建好后面发现图片的跨域问题一直解决不了,最后因为时间关系一次伟大的尝试就此放弃。还是太菜呀QAQ~
 然后临时准备做一个破解滑块验证码来应付,刚刚开始再网上搜索也是一大堆教程,然后我花了半天时间踩坑,(ps:环境搭建第一步就把我坑惨了,最后把python环境和pycharm卸载重装了(:
 然后我很开心去网上找各种教程,开始兴奋的copy,到最后发现教程里面给的网站只要是极验的滑动验证码基本上都已经不能破解了,!!! 实现方法没错,但是极验的验证码已经使用了机器学习,只有刚刚开始可能是滑块验证码,过一两次可能就是不是滑块验证码而是其他的选字或者选图的验证码了,然后我去换极验的其他网站,如果换其他网站又得重新写自动化脚本,这个其实还好,但是又要去重新截验证图片,而且不同的网站的登录窗口位置不一样,所以GG!!!
 当时我也基本崩溃了,这么多网站我上哪找一个没有机器学习的简单的滑块验证码,对我一个小白太说真的太不友好了QAQ,但是最后还是找到了!(小声bb,幸亏腾讯这个大厂还没有使用机器学习到这上面,应该是腾讯不想使用别人的接口吧,也不知道腾讯多久才会升级这个滑块验证码,它升级之时,也就是本篇博客废除之时了。)
 折腾这么久也算是圆满结局了,作为一个小白真的太不容易了。也体会到了python的便捷性,真的是太后悔没有用python写个刷课脚本了。然后还有一点学习心得就是,学习一定要及时总结,尤其是学计算机这一块,一定要多写写博客及时总结才会促使自己不断进步,真的太后悔当初没有一直保持好这个习惯,老年人落泪呜呜呜~~~~~

参考

https://www.cnblogs.com/xtjiaoyou/archive/2020/03/22/12530851.html
http://www.mamicode.com/info-detail-2172754.html

  • 14
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值