通過selenium突破極驗驗證實現登入

小郭pp發表於2018-06-20

筆者是一名初入Python爬蟲的小白,通過書籍(靜覓大神出的書籍)的方式學了下突破驗證的方式實現模擬登入有此型別的應用的知識。該方法不涉及任何商業關係,如果有違規行為麻煩聯絡下筆者

實現的步驟分為3步:

  • 1.使應用出現驗證的完整圖片和帶有缺口的圖片
  • 2.識別缺口的位置
  • 3.模擬拖動滑塊至缺口處,完成驗證

引入相關的庫

from selenium import webdriver;
from selenium.webdriver.support.wait import WebDriverWait;
from selenium.webdriver.support import expected_conditions as EC;
from selenium.webdriver.common.by import By;
from selenium.webdriver import ActionChains;
import time;
from PIL import Image;
from io import BytesIO;
複製程式碼

我們定義一個類來實現相關的操作並且定義一些配置

EMAIL='xxx'  #賬號
PASSWORD='xxx'  #密碼(只是簡單的處理)
BORDER=6; #開始滑動的小塊與左邊緣的距離
INIT_LEFT=60; #開始從X軸方向即x=60開始檢測缺口的位置
複製程式碼
class GrackGeetest(object):
    
    def __init__(self):
        #這邊我們開始定義一些相關的引數資訊(我們用登入極驗官網來做例子,其它的方式類似)
        self.url='https://auth.geetest.com/login/';
        self.browser=webdriver.Chrome();
        self.wait=WebDriverWait(self.browser,20);
        self.email=EMAIL;
        self.password=PASSWORD;
        

#實現步驟1相關方法:
    def getGeetestButton(self): 
        #獲取點選可以使現驗證圖出現的按鈕節點元素並返回
        button=self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'geetest_radar_tip')));
        return button;
    
    #獲取驗證圖在網頁中的位置並以元組的方式返回
    def getImagePosition(self):
        geetestImage=self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_canvas_img')));
        time.sleep(2);
        location=geetestImage.location;
        size=geetestImage.size;
        top,bottom,left,right=location['y'],location['y']+size['height'],location['x'],location['x']+size['width'];
        return (top,bottom,left,right);
    
    #擷取當前頁面
    def getChromePage(self):
        pageShot=self.browser.get_screenshot_as_png();
        pageShot=Image.open(BytesIO(pageShot));
        return pageShot;
    
    #從網頁中擷取該驗證圖片並返回
    def getGeetestImage(self,name='geetest.png'):
        top,bottom,left,right=self.getImagePosition();
        #擷取當前頁面的圖片
        pageShot=self.getChromePage();
        #擷取其中出現的驗證圖的位置
        captchaImage=pageShot.crop((left,top,right,bottom));
        captchaImage.save(name);#儲存到當前的資料夾中
        return captchaImage;
    
#實現步驟2相關方法:識別缺口位置
    def getSlider(self):
        #獲取可拖動的滑塊物件
        slider=self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'geetest_slider_button')));
        return slider;
    
    #通過對比2張圖的畫素點的差距得出缺口位置
    def getGap(self,image1,image2):
        left=60; 
        #size[0]->width,size[1]->height
        for i in range(left,image1.size[0]):
            for j in range(image1.size[1]):
                if not self.isPixelEqual(image1,image2,i,j):
                    #因為小滑塊和缺口是同一條水平線上的所以就只取x軸方向上的值
                    left=i;
                    return left;
        return left;
        
    
    def isPixelEqual(self,image1,image2,x,y):
        #判斷2個畫素是否相同
        pixel1=image1.load()[x,y]; #pixel1,pixel2為rgb值,是一個元組
        pixel2=image2.load()[x,y];
        #閥值當超出這個閥值的時候則證明這2個畫素點不匹配,為缺口的左上角的畫素點
        threshold=60;
        if abs(pixel1[0]-pixel2[0])<threshold and abs(pixel1[1]-pixel2[1])<threshold and abs(pixel1[2]-pixel2[2])<threshold :
            return True;
        else:
            return False;
    
    #步驟三相關方法:最關鍵的一步也是突破極驗驗證機器學習演算法的一步
    #採用物理中物體的分階段改變加速度的方式,這裡採用先加速後減速的方式
    #公式 x=v0*t+1/2*a*t*t  v=v0+a*t
        
    def getTrack(self,distance):
        #distance偏移量
        #移動軌跡
        tranck=[];
        #當前位移
        current=0;
        #開始減速的閥值
        mid=distance*4/5;
        #計算間隔
        t=0.2;
        #初速度
        v=0;
        while current<distance:
            if current<mid:
                a=2;
            else:
                #開始減速
                a=-3;
            #初速度
            v0=v;
            #當前速度
            v=v0+a*t;
            #位移
            move=v0*t+1/2*a*t*t;
            #當前位移
            current+=move;
            #加入軌跡
            track.append(round(move));
        return track; 
            
    #按照運動軌跡移動滑塊
    def moveToGap(self,slider,tracks):
        #拖動滑塊到缺口處
        ActionChains(self.browser).click_and_hold(slider).perform();
        for x in tracks:
            ActionChains(self.browser).move_by_offset(xoffset=x,yoffset=0).perform();
        time.sleep(0.5);
        ActionChains(self.browser).release().perform();
            
    #最後模擬點選登入應用就行了
    def login(self):
        button=self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#base > div.content-outter > div > div > div:nth-child(3) > div > form > div:nth-child(5) > div > button')));
        button.click();
        time.sleep(10);
    
    
    #接下來直接實現通過一個方法將這整個過程連線起來
    def sendUserAndPassword(self):
        self.browser.get(self.url);
        #通過類選擇器,我是直接在瀏覽器那邊複製過來的,所以比較長,可以通過其它方式得到該元素(右鍵那個網頁元素就有一些選擇可以看看哈)
        email=self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#base > div.content-outter > div > div > div:nth-child(3) > div > form > div:nth-child(1) > div > div.ivu-input-wrapper.ivu-input-type.ivu-input-group.ivu-input-group-with-prepend > input')));
        password=self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#base > div.content-outter > div > div > div:nth-child(3) > div > form > div:nth-child(2) > div > div.ivu-input-wrapper.ivu-input-type.ivu-input-group.ivu-input-group-with-prepend > input')));
        email.send_keys(self.email);
        password.send_keys(self.password);
    
    
    
    
    def doVerifyLogin(self):
    #步驟1:
    
        #輸入賬號密碼
        self.sendUserAndPassword();
        #點選驗證按鈕
        verifyButton=self.getGeetestButton();
        verifyButton.click();
        #開始獲取2張驗證圖
        image1=self.getGeetestImage('geetest1.png');
        #點選小滑塊得到有缺口的驗證圖
        slider=self.getSlider();
        slider.click();
        #獲取帶缺口的驗證圖
        image2=self.getGeetestImage('geetest2.png');
    
    #步驟2:
        #獲取缺口位置
        gap=self.getGap(image1,image2);
        #缺口的位置需要減去那個小滑塊與左邊那一小段距離
        gap-=BORDER;
    
    #步驟3:
        #移動軌跡
        track=self.getTrack();
        #拖動滑塊
        self.moveToGap(slider,track);
        #最後判斷是否成功了,不成功就重新操作這一過程
        try:
            success = self.wait.until(
                EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), '驗證成功'))
            print(success)
            # 失敗後重試
            if not success:
                self.doVerifyLogin()
            else:
                self.login()
        except:
            self.doVerifyLogin();
    
    if __name__ == '__main__':
        crack = GrackGeetest();
        crack.doVerifyLogin();
複製程式碼

以上就是完整的程式碼了,同時需要安裝ChromeDriver,安裝的具體過程找下搜尋引擎問問

相關文章