• 超级强大的破解极验滑动验证码--讲解非常详细


    废话不多说,好久不写博客了,难得用心写了一下,怎么也给自己留个念想。。。虽然工作上暂时用不到。。。

    其实网上已经好多了,我写的时候遇到的问题也参考了不少其他人的代码,嗯。。,那我这个就叫小白解读帖吧,讲解还算详细

    网站说:

    少于150字的随笔不允许发布到网站首页
    尼玛,我下面的代码数量何止150个字啊,哎,开始凑字数,你们懂的,作为毫无营养的废话,我决定用颜色把他。。。靠,我说到哪了,嗯,反正也是废话,凑字而已。日啊
    自己都看不见的文字,让我想起了m。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    我们开发的说,能写一行代码的绝对不写两行,但凡能携程这样的都是可以优化的,好比lamabada变大时,我觉得我打错了。好吧,好吧,反正我也看不见。。凑够字了吧。。
    # -*- coding: utf-8 -*-
    """
    @author:你们的龙哥
    @2018-06-21
    规则只匹配极验的官网,若使用破解验证码的规则,仍需要根据实际项目情况做调整
    建议:本程序提供的是思路,无需修改本程序代码
    写代码过程中遇到的问题:
    1、chrome driver要下载,并配置好,过程忘记了,请百度。driver的版本一定要和chrome的版本对应。。。
    2、chrome浏览器不在环境变量的,要把第32行(大概位置)注释解开,并填入自己电脑chrome的位置。要不就会报错,二进制文件找不到之类的。
    3、reload(sys)要加,不然不能识别中文
    4、location定位的时候,经常出白图,是因为save_screenshot()截屏的时候,保存的图片像素为实际像素的2倍,这个百度了一下,有人说是高分屏的原因,因为我用的mac?
    5、第一次识别失败后,经常会报错,好像是时间超时之类的,我try掉了。想踩雷的,可以去掉所有的try自己玩一下。
    6、本代码经过本机的极限测试,连续尝试40多次后依然可以正常运行,然后我就手动结束了测试。
    """
    
    from PIL import Image
    from selenium import webdriver
    from selenium.webdriver import ActionChains
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.wait import WebDriverWait
    from selenium.webdriver.chrome.options import Options
    import logging
    import time
    import sys
    reload(sys)
    sys.setdefaultencoding('utf8')
    
    # 本段为指定chrome浏览器的位置,如果chrome安装的时候放到了环境变量中,应该用不到这样写,注释掉就可以
    # 果然,把chrome移动到应用程序里面后,自动就配置到环境变量了(mac本)
    chrome_options = Options()
    # chrome_options.binary_location = r"/Users/qmp/Downloads/Google Chrome.app/Contents/MacOS/Google Chrome"
    
    
    class CrackGeetest():
        """
        实现了正常登陆、错误重试、多次重试后刷新图片的功能。。。除非规则和页面结构变更,要不基本走不到多次刷新图片重试这一步
        没有对crack()和fail_again()这两个类方法进行精简,是希望程序的运行步骤清晰明了。。。
        """
        def __init__(self,url,email,password,threshold=60,left=57,deviation=6):
            self.url = url
            self.driver = webdriver.Chrome(chrome_options=chrome_options)
            self.wait = WebDriverWait(self.driver,10)
            self.email = email
            self.password = password
            self.threshold = threshold #验证码图片对比中RGB的差值,可调
            self.left = left #验证码图片的对比中的起始坐标,即拖动模块的右边线位置
            self.deviation = deviation # 偏移量,这个值是多次测试得出的经验值
            self.count = 1
    
        def close_win(self):
            self.driver.close()
    
        def get_geetest_button(self):
            """
            点击按钮,弹出没有缺口的图片
            :return: 返回按钮对象
            """
            button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_radar_tip')))
            return button
    
        def get_slider(self):
            """
            获取滑块
            :return: 滑块对象
            """
            slider = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_slider_button')))
            return slider
    
        def get_snap(self):
            """
            对整个网页截图,保存图片,然后用PIL.Image拿到图片对象
            :return: 图片对象
            """
            self.driver.save_screenshot('极验验证过程中产生的图片.png')
            page_snap_obj = Image.open('极验验证过程中产生的图片.png')
            return page_snap_obj
    
        def get_image(self,name='captcha.png'):
            """
            从网页的网站截图中,截取验证码图片
            :return: 验证码图片对象
            """
            img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))
            time.sleep(2)  # 保证图片刷新出来
            location = img.location
            size = img.size
            top = location['y']
            bottom = location['y'] + size['height']
            left = location['x']
            right = location['x'] + size['width']
            page_snap_obj = self.get_snap()
    
            #这里强调一下:大概由于高分屏的原因,网页的截图是实际像素的2倍,所以验证码定位也要相应*2
            # crop_imag_obj = page_snap_obj.crop((left,top,right,bottom))
            crop_imag_obj = page_snap_obj.crop((2 * left, 2 * top, 2 * right, 2 * bottom))
            size = 258, 159
            crop_imag_obj.thumbnail(size)
            #实际生产环境下就不需要保存这张图片了
            # crop_imag_obj.save(name)
            return crop_imag_obj
    
        def open(self):
            """
            打开网页输入用户名和密码
            :return:
            """
            self.driver.get(self.url)
            email = self.wait.until(EC.presence_of_element_located((By.ID, 'email')))
            password = self.wait.until(EC.presence_of_element_located((By.ID, 'password')))
            email.send_keys(self.email)
            password.send_keys(self.password)
    
        def get_distance(self,image1, image2):
            """
            拿到滑动验证码需要移动的距离
            :param image1: 没有缺口的图片对象
            :param image2: 带缺口的图片对象
            :return: 需要移动的距离
            """
            i = 0
            for i in range(self.left, image1.size[0]):
                for j in range(image1.size[1]):
                    rgb1 = image1.load()[i, j]
                    rgb2 = image2.load()[i, j]
                    res1 = abs(rgb1[0] - rgb2[0])
                    res2 = abs(rgb1[1] - rgb2[1])
                    res3 = abs(rgb1[2] - rgb2[2])
                    if not (res1 < self.threshold and res2 < self.threshold and res3 < self.threshold):
                        return i - self.deviation  # 误差矫正
            logging.debug('未识别出验证码中的不同位置,或图片定位出现异常')
            return i  # 如果没有识别出不同位置,则象征性的滑动,以刷新下一张验证码
    
        def get_tracks(self,distance):
            """
            拿到移动轨迹,模仿人的滑动行为,先匀加速后均减速
            匀变速运动基本公式:
            ①:v=v0+at
            ②:s=v0t+½at²
            ③:v²-v0²=2as
            :param distance:需要移动的距离
            :return:存放每0.3秒移动的距离
            """
            distance += 20  # 先滑过一点,最后再反着滑动回来
            # 初速度
            v = 0
            # 单位时间为0.3s来统计轨迹,轨迹即0.3s内的位移
            t = 0.3
            # 位移/轨迹列表,列表内的一个元素代表0.3s的位移
            forward_tracks = []
            # 当前位移
            current = 0
            # 到达mid值开始减速
            mid = distance * 4 / 5
            while current < distance:
                if current < mid:
                    # 加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细
                    a = 2
                else:
                    a = -3
                # 初速度
                v0 = v
                # 0.3秒时间内的位移
                s = v0 * t + 0.5 * a * (t ** 2)
                # 当前的位置
                current += s
                # 添加到轨迹列表,round()为保留一位小数且该小数要进行四舍五入
                forward_tracks.append(round(s))
                # 速度已经达到v,该速度作为下次的初速度
                v = v0 + a * t
    
            # 反着滑动到准确位置
            back_tracks = [-3, -3, -2, -2, -2, -2, -2, -1, -1, -1]  # 总共等于-20
            return {'forward_tracks': forward_tracks, 'back_tracks': back_tracks}
    
        def login(self):
            """
            登录
            :return: None
            """
            submit = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'login-btn')))
            submit.click()
            print('登录成功')
            logging.debug('登陆成功')
    
        def crack(self):
            """
            程序运行流程。。。
            :return:
            """
            #步骤一:输入同户名密码
            self.open()
    
            #步骤二:点击按钮,弹出没有缺口的图片
            button = self.get_geetest_button()
            button.click()
            time.sleep(0.5)
    
            # 步骤三:拿到没有缺口的图片
            image1 = self.get_image('captcha1.png')
    
            # 步骤四:点击托送按钮,弹出有缺口的图片
            slider = self.get_slider()
            slider.click()
    
            #步骤五:拿到有缺口的图片
            image2 = self.get_image('captcha2.png')
    
            #步骤六:对比两张图片的所有RBG像素点,得到不一样像素点间的差值,即要移动的距离
            distance = self.get_distance(image1, image2)
    
            # 步骤七:模拟人的行为习惯(先匀加速拖动后匀减速拖动),把需要拖动的总距离分成一段一段小的轨迹
            tracks = self.get_tracks(distance)
    
            # 步骤八:按照轨迹拖动,完成验证
            button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_slider_button')))
            ActionChains(self.driver).click_and_hold(slider).perform()
    
            # 正常人类总是自信满满地开始正向滑动,自信地表现是疯狂加速
            for track in tracks['forward_tracks']:
                ActionChains(self.driver).move_by_offset(xoffset=track, yoffset=0).perform()
    
            # 结果傻逼了,正常的人类停顿了一下,回过神来发现,卧槽,滑过了,然后开始反向滑动
            time.sleep(0.6)
            for back_track in tracks['back_tracks']:
                ActionChains(self.driver).move_by_offset(xoffset=back_track, yoffset=0).perform()
    
            # 小范围震荡一下,进一步迷惑极验后台,这一步可以极大地提高成功率
            time.sleep(0.3)
            ActionChains(self.driver).move_by_offset(xoffset=3, yoffset=0).perform()  # 先移动去一点
            time.sleep(0.4)
            ActionChains(self.driver).move_by_offset(xoffset=-3, yoffset=0).perform()  # 再退回来,模仿人的行为习惯
    
            time.sleep(0.6)  # 0.6秒后释放鼠标
            ActionChains(self.driver).release().perform()
    
            try:
                success = self.wait.until(
                    EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip'), '验证成功'))
                self.login()
                time.sleep(5)
                self.close_win()
            except:
                # 位置没定位好,或者时间太长了,所以本次失败,进入下一轮
                self.fail_again()
    
    
        def fail_again(self):
            """
            失败重试功能,加了好几个try,防止报错,使程序运行下去。在故意改变误差值(偏移量)的情况下,目前只尝试到40多次,遇到的报错都位于步骤三,程序暂无问题
            :return:
            """
    
            # 步骤二:点击刷新按钮,弹出没有缺口的图片,其实貌似也许大概,无需手动点刷新,失败后自动就刷新了,但我就这么写了。。。
            try:
                button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_refresh_1')))
                button.click()
            except Exception as e:
                print e
                print '尝试次数过多,刷新一次图片-步骤二'
                button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_reset_tip_content')))
                button.click()
                time.sleep(2)
                self.fail_again()
    
            # 步骤三:拿到没有缺口的图片
            try:
                time.sleep(0.5)
                image1 = self.get_image('captcha1.png')
            except Exception as e:
                print e
                print '尝试次数过多,刷新一次图片-步骤三'
                button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_reset_tip_content')))
                button.click()
                time.sleep(2)
                self.fail_again()
    
            # 步骤四:点击托送按钮,弹出有缺口的图片
            try:
                slider = self.get_slider()
                slider.click()
            except Exception as e:
                print e
                print '尝试次数过多,刷新一次图片-步骤四'
                button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_reset_tip_content')))
                button.click()
                time.sleep(2)
                self.fail_again()
    
            # 步骤五:拿到有缺口的图片
            try:
                image2 = self.get_image('captcha2.png')
            except Exception as e:
                print e
                print '尝试次数过多,刷新一次图片-步骤五'
                button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_reset_tip_content')))
                button.click()
                time.sleep(2)
                self.fail_again()
    
            # 步骤六:对比两张图片的所有RBG像素点,得到不一样像素点间的差值,即要移动的距离
            distance = self.get_distance(image1, image2)
    
            # 步骤七:模拟人的行为习惯(先军加速拖动后匀减速拖动),把需要拖动的总距离分成一段一段小的轨迹
            tracks = self.get_tracks(distance)
    
            # 步骤八:按照轨迹拖动,完全验证
            button = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_slider_button')))
            ActionChains(self.driver).click_and_hold(slider).perform()
    
            # 正常人类总是自信满满地开始正向滑动,自信地表现是疯狂加速
            for track in tracks['forward_tracks']:
                ActionChains(self.driver).move_by_offset(xoffset=track, yoffset=0).perform()
    
            # 结果傻逼了,正常的人类停顿了一下,回过神来发现,卧槽,滑过了,然后开始反向滑动
            time.sleep(0.6)
            for back_track in tracks['back_tracks']:
                ActionChains(self.driver).move_by_offset(xoffset=back_track, yoffset=0).perform()
    
            # 小范围震荡一下,进一步迷惑极验后台,这一步可以极大地提高成功率
            time.sleep(0.3)
            ActionChains(self.driver).move_by_offset(xoffset=-4, yoffset=0).perform()  # 先移动去一点
            time.sleep(0.4)
            ActionChains(self.driver).move_by_offset(xoffset=4, yoffset=0).perform()  # 再退回来,模仿人的行为习惯
    
            time.sleep(0.6)  # 0.5秒后释放鼠标
            ActionChains(self.driver).release().perform()
            print self.count
            try:
                success = self.wait.until(
                EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip'), '验证成功'))
                self.login()
                time.sleep(5)
                self.close_win()
            except:
                self.count+=1
                if self.count<100:
                    self.fail_again()
                else:
                    self.close_win()
    
    
    if __name__ == '__main__':
        url = 'https://account.geetest.com/login'
        email = '马赛克了吗'
        password = '赛克了'
        threshold = 60 #RGB色差
        left = 57 #起始位置
        deviation = 7 #偏移量,误差
        crack = CrackGeetest(url, email, password, threshold, left, deviation)
        crack.crack()

    我去,赶紧马赛克账号密码,差点把自己暴露了

  • 相关阅读:
    idHTTP使用代理IP
    Delphi的TRegistry注册表类方法详解
    判断WebBrowser是否加载完成.
    vs2010 安装记
    Delphi中destroy, free, freeAndNil, release用法和区别
    IdHTTP处理HTTP 302遇到的问题
    passcal 多线程例子,线程管理,创建,暂停,中止,等待等。。。
    passcal try嵌套
    passcal 编写多线程程序
    用一个 Byte 数表示 8 个复选框的选择状态
  • 原文地址:https://www.cnblogs.com/llhtjwq/p/9210694.html
Copyright © 2020-2023  润新知