zoukankan      html  css  js  c++  java
  • 滑块验证码(腾讯)——基于selenium,pillow实现

      与网上的其他滑块验证码不同,腾讯的验证码可以直接通过url请求得到,只需要对url进一步分析,提取出验证码原图的地址,并将图片下载即可。

    但据我观察,该url似乎是有两种不同类型的地址格式,需要具体分析。这里,选择其中一种进行实验,源码在文章末尾。

        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')
            #  分析图片地址,发现原图地址可以通过阴影图地址改动获取
            src_bg = re.sub('_1_', '_0_', src)
            urlretrieve(src, 'img1.png')
            urlretrieve(src_bg, 'img2.png')
    获取图片,保存到本地

      有一个需要注意的问题,下载到本地的图片对原图进行了放大,所以要对图片尺寸进行调整还原,保证后面计算出的偏移值的正确性

    def resize_img(self, img):
            """
            下载的图片把网页中的图片进行了放大,所以将图片还原成原尺寸
            :param img: 图片
            :return: 返回还原后的图片
            """
            a = 2.428  # 通过本地图片与原网页图片的比较,计算出的缩放比例
            (x, y) = img.size
            x_resize = int(x // a)
            y_resize = int(y // a)
            img = img.resize((x_resize, y_resize), Image.ANTIALIAS)
    还原图片尺寸

      此时,已经拿到原图和阴影图,只需要进行像素比较即可,网上有其他相关教程,可参考

        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
            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):
            '''
                获取缺口的偏移量
            '''
            offset = None
            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):
                        offset = i
                        return offset
            return offset
    计算偏移值

      最后,通过selenuim的动作链,模拟滑块拖动

        def operate_slider(self, track):
            """
            拖动滑块
            :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()
            # 按正向轨迹移动
            for i in track:
                ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
                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()
    模拟滑块拖动

      下面附上完整的代码,仅供参考,不足之处,欢迎大家指正

    import time
    import re
    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/'
            driver_path = r'C:UsersxiaodengtangAppDataLocalGoogleChromeApplicationchromedriver.exe'
            self.driver = webdriver.Chrome(executable_path=driver_path)
            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')
            #  分析图片地址,发现原图地址可以通过阴影图地址改动获取
            src_bg = re.sub('_1_', '_0_', src)
            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: 返回还原后的图片
            """
            a = 2.428  # 通过本地图片与原网页图片的比较,计算出的缩放比例
            (x, y) = img.size
            x_resize = int(x // a)
            y_resize = int(y // a)
            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
            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):
            '''
                获取缺口的偏移量
            '''
            offset = None
            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):
                        offset = i
                        return offset
            return offset
    
        def get_track(self, offset):
            '''
            计算滑块的移动轨迹
            '''
            offset -= 30  # 滑块并不是从0开始移动,有一个初始值
            a = offset / 4
            track = [a, a, a, a]
            return track
    
        def shake_mouse(self):
            """
            模拟人手释放鼠标抖动
            :return: None
            """
            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):
            """
            拖动滑块
            :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()
            # 按正向轨迹移动
            for i in track:
                ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
                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):
            '''
            实现主要的登陆逻辑
            '''
            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 = '123548658'
        password = 'yanzhengma'
        qq.login(account, password)
    完整代码
  • 相关阅读:
    css中vertical-align(垂直对齐)的使用
    CSS教程:div垂直居中的N种方法[转]
    前后端分离开发部署模式
    <a>标签的href和onclick属性
    css 字体样式
    谷歌开发者工具界面介绍
    cps和dsp渠道手法的研究
    网络资源汇总
    DataWorks(数据工场)
    vue入门学习笔记
  • 原文地址:https://www.cnblogs.com/xtjiaoyou/p/12530851.html
Copyright © 2011-2022 走看看