1. 程式人生 > >破解滑動驗證碼(極驗)

破解滑動驗證碼(極驗)

from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium import webdriver
from io import BytesIO
from PIL import Image
import random
import time

class CrackGesstest(object):
    def __init__(self):
        self.url = 'https://www.geetest.com/type/'
        self.driver = webdriver.Chrome()
        self.wait = WebDriverWait(self.driver, 10)

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

    def openPage(self):
        """
        開啟頁面,顯示出滑動驗證碼
        :return:
        """
        # 開啟頁面
        self.driver.get(self.url)
        # 選擇滑動行為驗證
        slide_button = self.wait.until(
            EC.element_to_be_clickable((By.XPATH, '//div[@class="products-content"]//li[2]')))
        slide_button.click()
        # 點選驗證按鈕
        verifi_button = self.wait.until(
            EC.element_to_be_clickable((By.XPATH, '//div[@class="geetest_radar_tip"]')))
        verifi_button.click()

    def getImage(self, img_name):
        """
        獲得驗證碼圖片
        :param img_name: 圖片名稱.png
        :return: image
        """
        # 獲得驗證碼圖片
        canvas = self.wait.until(
            EC.presence_of_element_located((By.XPATH, '//canvas[@class="geetest_canvas_slice geetest_absolute"]')))
        # 驗證碼位置
        position = self.imagePosition(canvas)
        # 螢幕截圖
        screenshot = self.driver.get_screenshot_as_png()
        screenshot = Image.open(BytesIO(screenshot))
        # 從螢幕截圖中擷取驗證碼圖片
        image = screenshot.crop(position)
        image.save(img_name)
        print('圖片儲存完畢')
        return image

    def imagePosition(self, image_tag):
        """
        獲得圖片的位置資訊
        :param image_tag:圖片節點
        :return: 位置元組(left, top, right, bottom)
        """
        left = image_tag.location['x']
        # 根據實際情況改變資料大小(否則截圖位置可能與實際驗證碼所在位置不一樣,具體為什麼還不懂)
        top = image_tag.location['y'] / 2
        right = left + image_tag.size['width']
        bottom = top + image_tag.size['height']
        return (left, top, right, bottom)

    def getDistante(self, img1, img2):
        """
        獲得圖片不同之處,距離圖片左邊的距離
        :param img1:殘缺圖片
        :param img2:完整圖片
        :return: 距離
        """
        # 大概比滑塊長度大一點
        distance = 60
        for i in range(distance, img1.size[0]):
            for j in range(1, img1.size[1]):
                if not self.isPixelEqual(img1, img2, i, j):
                    distance = i
                    return distance

        return distance


    def isPixelEqual(self,img1, img2, i, j):
        """
        判斷兩個畫素點是否相同
        :param img1:殘缺圖片
        :param img2:完整圖片
        :param i: 畫素橫座標
        :param j: 畫素縱座標
        :return: bool
        """
        # 若某種顏色相差大於60,則認為不同
        xxx = 65
        rgb1 = img1.load()[i, j]
        rgb2 = img2.load()[i, j]
        if abs(rgb1[0] - rgb2[0]) < xxx and abs(rgb1[1] - rgb2[1]) < xxx and abs(rgb1[2] - rgb2[2]) < xxx:
            return True
        return False

    def getTracks(self, distance):
        """
        根據滑動距離設計滑動行為,避免被機器識別
        :param distance:滑塊應該滑動的距離
        :return:forward_tracks(前向滑動距離),back_tracks(後向滑動距離)
        """
        # 初始位置
        x = 0
        # 初始速度
        v = 0
        # 每次滑動時間t
        t = [0.2, 0.1]
        # 前向滑動行為
        forward_tracks = []
        # 後項滑動行為,根據實際可進行更改
        back_tracks = [-6, -3, 2, 2, -3, -2, -2]
        # 取前五分之三進行加速,後面減速
        mid = distance * 3 / 5
        while x < distance + 5:
            if x < mid:
                a = 2
            else:
                a = -3
            i = random.randint(0, 1)
            # 每次滑動距離
            s = v * t[i] + 0.5 * a * t[i] * t[i]
            # 更新速度v
            v = v + a * t[i]
            # 更新當前位置
            x += s
            # 更新前向滑動行為
            forward_tracks.append(round(s))

        return forward_tracks, back_tracks

    def moveToGap(self, forward_tracks, back_tracks):
        """
        根據滑動行為,移動滑塊到殘缺地方
        :param forward_tracks: 前向滑動行為
        :param back_tracks: 後向滑動行為
        :return:
        """
        # 模擬滑鼠點選住滑塊
        button = self.wait.until(EC.presence_of_element_located((By.XPATH, '//div[@class="geetest_slider_button"]')))
        ActionChains(self.driver).click_and_hold(button).perform()
        # 拖動滑塊
        for track in forward_tracks:
            ActionChains(self.driver).move_by_offset(xoffset=track, yoffset=0).perform()
        time.sleep(0.1)
        for back_track in back_tracks:
            ActionChains(self.driver).move_by_offset(xoffset=back_track, yoffset=0).perform()
        time.sleep(0.2)
        # 釋放滑鼠
        ActionChains(self.driver).release().perform()

    def crackStart(self):
        """
        進行破解驗證碼
        :return:
        """
        self.openPage()
        # 獲得殘缺驗證碼圖片
        time.sleep(3)
        img_name = '1bg.png'
        bg_image = self.getImage(img_name)

        # 獲得完成驗證碼圖片
        self.driver.execute_script('document.querySelectorAll("canvas")[2].style=""')
        time.sleep(2)
        img_name = '1fullbg.png'
        fullbg_image = self.getImage(img_name)

        # 獲取距離
        distance = self.getDistante(bg_image, fullbg_image)
        print('distance:', distance)

        # 獲取滑動行為
        forward_tracks, back_tracks = self.getTracks(distance)
        print(forward_tracks)
        print(back_tracks)

        # 移動滑塊
        self.moveToGap(forward_tracks, back_tracks)

        time.sleep(10)


def main():
    crack = CrackGesstest()
    crack.crackStart()
    print('work done!')


if __name__ == '__main__':
    main()

在這裡插入圖片描述