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.小結

  好了,今天到這裏宏哥就簡單的介紹了一下博客園滑動的驗證登錄,其他的類似的登錄機制,你可以照貓畫虎即可實現登錄了。

 

您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波  推薦  不要忘記哦!!!

別忘了點 推薦 留下您來過的痕跡

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章