超級強大的破解極驗滑動驗證碼--講解非常詳細


廢話不多說,好久不寫博客了,難得用心寫了一下,怎么也給自己留個念想。。。雖然工作上暫時用不到。。。

其實網上已經好多了,我寫的時候遇到的問題也參考了不少其他人的代碼,嗯。。,那我這個就叫小白解讀帖吧,講解還算詳細

網站說:

少於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()

我去,趕緊馬賽克賬號密碼,差點把自己暴露了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM