破解滑動驗證碼(極驗)
阿新 • • 發佈:2018-11-12
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()