专栏首页北京宏哥What?废柴, 模拟登陆,代码控制滑动验证真的很难吗?Are you kidding???

What?废柴, 模拟登陆,代码控制滑动验证真的很难吗?Are you kidding???

1.简介

  在前边的python接口自动化的时候,我们由于博客园的登录机制的改变,没有用博客园的登录测试接口。那么博客园现在变成了滑动验证登录,而且现在绝大多数的登录都变成这种滑动验证和验证码的登录验证机制。我们真的没有其他办法解决这种验证机制的登录了吗?真的是束手无策了吗?答案是:NO,今天宏哥教你如何用代码来模拟鼠标滑动,最终验证成功后,最后成功登录。那么怎么做了,思路了???

2.我们首先理解滑动验证的原理

滑动验证难点 1.电脑如何自动点击滑动块 2.电脑如何检测 缺口位置(如图;)

3.解决这两个问题方法

  1. 如何自动点击滑动块,也就是图中的左下方圈起来的位置,我们可以使用selenium
  2. 怎么计算缺口的位置,我们可以通过PIL库的image

4.博客园登录

  既然有了解决方法,我们看一下博客园的登录思路:

(1)首先我们需要打开登录页面,并输入用户名和密码,点击登录按钮,弹出验证码图片;(这个比较简单也容易实现)

(2)其次我们需要获取2张验证码图片,带缺口和不带缺口;

(3)最后我们需要获取缺口位置。遍历带缺口的图片和不带缺口的图片的每个像素,利用 is_pixel_equal() 方法判断两张图片同一位置的像素是否相同。比较两张图 RGB 的绝对值是否均小于定义的阈值 thresold。如果绝对值均在阈值之内,则代表像素点相同,继续遍历。否则代表不相同的像素点,就是缺口的位置。   通过对比两张图片可以发现,两张图片有两处明显不同的地方:一个是待拼合的滑块,一个是缺口。滑块的位置会出现在左边位置,缺口会出现在与滑块同一水平线的位置,所以缺口一般会在滑块的右侧。如果要寻找缺口,直接从滑块右侧寻找即可。这里直接设置遍历的起始横坐标为60,也就是从滑块的右侧开始识别,这样识别出的结果就是缺口的位置。

下图就是用来说明如何对比图片:

思路我们清楚了,那我们就开始撸代码吧。

4.1代码实现:

4.2参考代码:

# coding=utf-8?

# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2019-5-13
@author: 北京-宏哥   QQ交流群:705269076
Project: What?废柴, 模拟登陆,代码控制滑动验证真的很难吗?Are you kidding???
'''

# 3.导入模块
import time
from io import BytesIO
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

USERNAME = '博客园的username'
PASSWORD = '博客园的password'
BORDER = 6

class CrackGeetest():
    def __init__(self):
        self.url = 'https://passport.cnblogs.com/user/signin'
        self.url = 'https://passport.cnblogs.com/user/signin'
        self.browser = webdriver.Chrome('E:\\untitled\\automation_framework_demo\\tools\chromedriver.exe')
        # self.browser.maximize_window()  # 将Chrome窗口放大
        self.wait = WebDriverWait(self.browser, 20)
        self.username = USERNAME
        self.password = PASSWORD

    def __del__(self):
        self.browser.close()

    def get_login_button(self):
        """
        获取登录按钮,调出极验验证码
        :return: 登录按钮对象
        """
        #button_login = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'button')))
        button_login = self.wait.until(EC.element_to_be_clickable((By.ID, 'submitBtn')))
        return button_login

    def get_geetest_button(self):
        """
        获取初始验证按钮,即点击按钮进行验证
        :return: 按钮对象
        """
        button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
        return button

    def get_position(self, flag):
        """
        获取验证码位置
        :return: 验证码位置元组
        """
        img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))
        fullbg = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "geetest_canvas_fullbg")))
        time.sleep(2)

        if flag:
            self.browser.execute_script(
                "arguments[0].setAttribute(arguments[1], arguments[2])", fullbg, "style", "")
            print("获取不带缺口的图片成功")
        else:
            self.browser.execute_script(
                "arguments[0].setAttribute(arguments[1], arguments[2])", fullbg, "style", "display: none")
            print("获取带缺口的图片成功")

        location = img.location     # 图像位置
        size = img.size             # 图像大小
        top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']
        return (top, bottom, left, right, size)

    def get_screenshot(self):
        """
        获取网页截图
        :return: 截图对象
        """
        screenshot = self.browser.get_screenshot_as_png()
        screenshot = Image.open(BytesIO(screenshot))
        return screenshot

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

    def get_geetest_image(self, flag, name='captcha.png'):
        """
        获取验证码图片
        :return: 图片对象
        """
        top, bottom, left, right, size= self.get_position(flag)
        print('验证码位置', top, bottom, left, right, size)
        screenshot = self.get_screenshot()
        # 根据验证码图像位置获取验证码图像
        captcha = screenshot.crop((left, top, right,bottom))
        #captcha.save(name)
        return captcha

    def open(self):
        """
        打开网页输入用户名密码
        :return: None
        """
        self.browser.get(self.url)
        username = self.wait.until(EC.presence_of_element_located((By.ID, 'LoginName')))
        password = self.wait.until(EC.presence_of_element_located((By.ID, 'Password')))
        username.send_keys(self.username)
        password.send_keys(self.password)

    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.is_pixel_equal(image1, image2, i, j):
                    # left = i
                    # return left
                    return i
        return left

    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]
        #print("piexl1", pixel1, "piexl2", pixel2)
        threshold = 60
        if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(
            pixel1[2] - pixel2[2]) < threshold:
            #print("True")
            return True
        else:
            #print("False")
            return False

    def get_track(self, distance):
        """
        根据偏移量获取移动轨迹
        :param distance: 偏移量
        :return: 移动轨迹
        """
        # 移动轨迹
        track = []
        # 当前位移
        current = 0
        # 减速阈值
        mid = distance * 4 / 5
        # 计算间隔
        t = 0.2
        # 初速度
        v = 0

        while current < distance:
            if current < mid:
                # 加速度为正2
                a = 2
            else:
                # 加速度为负3
                a = -3
            # 初速度v0
            v0 = v
            # 当前速度v = v0 + a * t
            v = v0 + a * t
            # 移动距离 x = v0*t + 1/2 * a * t^2
            move = v0 * t + 0.5 * 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 login(self):
        """
        登录
        :return: None
        """
        # submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'submitBtn')))
        # submit.click()
        # time.sleep(10)
        print('登录成功')

    def crack(self):
        # 输入用户名和密码
        self.open()
        # 点击登录按钮,调出验证按钮
        login_button = self.get_login_button()
        login_button.click()

        # 获取验证码图片,不带缺口
        image1 = self.get_geetest_image(True, 'captcha1.png')
        # 点按呼出缺口图片,获取滑块
        slider = self.get_slider()
        # slider.click()    # 现在不需要点击滑块即可呼出缺口图片
        # 获取带缺口的验证码图片
        image2 = self.get_geetest_image(False, 'captcha2.png')

        # 获取缺口位置
        gap = self.get_gap(image1, image2)
        print('缺口位置', gap)

        # 减去缺口位移
        gap -= BORDER

        # 获取移动轨迹
        track = self.get_track(gap)
        print('滑动轨迹', track)

        # 拖动滑块
        self.move_to_gap(slider, track)

        try:
            success = self.wait.until(
                EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_panel_success_title'), '通过验证'))
            print(success)
            #self.login()
        except Exception:
            self.crack()

        if success:
            print(success)
            self.login()


if __name__ == '__main__':
    crack = CrackGeetest()
    crack.crack()

4.3运行结果:

运行代码后,控制台打印如下图的结果

从运行结果,我们可以清楚的看到登录成功了,至此我们就可以完美破解,滑动验证问题。代码中宏哥设置的思路是重复验证,第一次失败,不要着急,程序会自动尝试第二次,以此类推,直到验证通过,登录成功。

5.小结

  好了,今天到这里宏哥就简单的介绍了一下博客园滑动的验证登录,其他的类似的登录机制,你可以照猫画虎即可实现登录了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Appium+python自动化(四十)-Appium自动化测试框架综合实践 - 代码实现(超详解)

    今天我们紧接着上一篇继续分享Appium自动化测试框架综合实践 - 代码实现。由于时间的关系,宏哥这里用代码给小伙伴演示两个模块:注册和登录。

    北京-宏哥
  • python接口自动化(四十二)- 项目结构设计之大结局(超详解)

      这一篇主要是将前边的所有知识做一个整合,把各种各样的砖块---模块(post请求,get请求,logging,参数关联,接口封装等等)垒起来,搭建一个房子。...

    北京-宏哥
  • 《一头扎进》系列之Python+Selenium框架设计篇5 - 价值好几K的框架,哎呦!这个框架还真有点料啊!!!

      其实,到前面这一篇文章,简单的Python+Selenium自动化测试框架就已经算实现了。接下来的主要是介绍,unittest管理脚本,如何如何加载执行脚本...

    北京-宏哥
  • 再议Python协程——从yield到asyncio

    协程,英文名Coroutine。 前面介绍Python的多线程,以及用多线程实现并发(参见这篇文章【浅析Python多线程】),今天介绍的协程也是常用的并发手段...

    用户1432189
  • Django(三):HttpReques

      当一个请求连接进来时,django会创建一个HttpRequest对象来封装和保存所有请求相关的信息,并且会根据请求路由载入匹配的视图函数。每个请求的视图函...

    py3study
  • PyQt5 非模态对话框(live 型)

    本篇介绍非模态“实时”(live)对话框。与上一篇讲的”apply型“非模态对话框的区别是,非模态“实时”(live)对话框没有任何按钮,且所做的任何改变会自动...

    用户6021899
  • python的tkinter编程(八)Entry组件的详细介绍,以登录界面作为讲解

    写一个按钮,绑定一个方法,当点击这个按钮的时候,就会执行这个方法,在这个方法里面 获取到对应的你输入的值,将获取到的值传到数据库里面进行比对,失败给一个返回的...

    一天不写程序难受
  • 致青春--Python实现俄罗斯方块

    俄罗斯方块游戏是世界上最流行的游戏之一。是由一名叫Alexey Pajitnov的俄罗斯程序员在1985年制作的,从那时起,这个游戏就风靡了各个游戏平台,而且俄...

    PM小王
  • 用Python做一个翻译软件

    前两天吃了平哥的一波狗粮,他给女朋友写了一个翻译软件,自己真真切切的感受到了程序员的浪漫。在学习requests请求的时候做过类似的Demo,给百度翻译发送一个...

    PM小王
  • 【人工智障入门实战1】测试对象的设计:基于 pygame 实现 Amazing-brick 游戏

    项目地址:https://github.com/PiperLiu/Amazing-Brick-DFS-and-DRL

    Piper蛋窝

扫码关注云+社区

领取腾讯云代金券