爬蟲:通過滑動或者點觸驗證碼的方法及實現(點觸+滑動)


一、極驗驗證碼(geetest)的三種驗證模式

以極驗官網登錄(https://auth.geetest.com/login/)為例:

 

 在點擊 “點擊按鈕進行驗證”按鈕后,會出現三種情況

1.點擊直接通過

在一段時間的前幾次登錄時,點擊按鈕,會直接通過驗證:

 

 

2.點擊出現滑動驗證碼

登錄幾次后,再次登錄時,就會出現滑動驗證碼:

 

 3.點擊出現點觸驗證碼:

登錄過多時,就會出現點觸驗證碼,這也是極驗最難的一種驗證碼:

 

 

 二、分析

利用Python的selenium模塊進行自動登錄。

1.判斷是否通過驗證:

有一個類名為geetest_success_radar_tip的div標簽,在加載后內容為空,驗證成功后,內容變為“驗證成功”,可以通過其內容來判斷,是否驗證成功。

 
        


下面是我截取的自己寫的一段代碼,主要是表達出含義,沒有截取所有代碼,理解就好:
    
self.success = self.browser.find_element_by_css_selector('.geetest_success_radar_tip') # 獲取顯示結果的標簽 def is_success(self): time.sleep(1) if self.success.text == "驗證成功": return True else: return False
 
        

 

 
        

 

 
        

 2.點擊直接通過

在點擊“點擊驗證按鈕”后,對是否驗證成功進行一次判斷,如果驗證通過,則可以進行下一步動作。

3.滑動驗證

滑動驗證的標簽是canvas,類名是geetest_canvas_slice,在點擊“點擊驗證”后,如果是滑動驗證,這個標簽將會被加載,如果不是,則這個標簽不會被加載。

所以可以通過查看是否存在這個標簽,來判斷驗證是否是滑動驗證:

    def is_slide(self):
        time.sleep(1)
        try:
            slie_img = self.browser.find_element_by_css_selector('canvas.geetest_canvas_slice')
            if slie_img:
                return slie_img
        except NoSuchElementException:
            return False

 

如果是滑動驗證,則可以通過比對有缺口圖片和原圖片,從而確定缺口的位置,然后模擬滑動滑塊,從而達到驗證的目的。具體操作,我在上一篇隨筆中已經寫過,請參考:
https://www.cnblogs.com/ohahastudy/p/11493971.html
 
        

4.點觸驗證

和滑動驗證類似,點觸驗證可以通過判斷是否存在類名為geetest_item_img的img標簽來判斷該驗證為點觸驗證。

 

 

 

    def is_pick(self):
        try:
            pick_img = self.browser.find_element_by_css_selector('img.geetest_item_img')
            return pick_img
        except NoSuchElementException:
            return False

 


保存該圖片后,我們可以發現,該圖片實際上是由兩部分組成:

 

 
        

 圖片中驗證碼的識別,我是通過超級鷹這個平台來識別的。按照超級鷹的文檔,將圖片發過去,並指明類型,在幾秒之后會返回一個結果,內容含有需要識別模塊的坐標,而其順序也是按照上圖中白色部分指定的順序,可以說是非常貼心了。

獲取坐標后,將坐標提取出來,然后通過模擬點擊即可。

三、具體實現:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from PIL import Image
from six import BytesIO
import time
from selenium.webdriver import ActionChains
from selenium.common.exceptions import NoSuchElementException
import requests
import chaojiying  #超級鷹提供的模塊,需要去官網下載


class Verifycode():

    def __init__(self):
        self.browser = webdriver.Chrome()

    def get_url(self, url, user, password):
        self.browser.get(url)
        self.browser.maximize_window()
        wait = WebDriverWait(self.browser, 10)
        wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_radar_btn')))
        user_input, pwd_input, *_ = self.browser.find_elements_by_css_selector('input.ivu-input')
        print(user_input)
        btn = self.browser.find_element_by_css_selector('.geetest_radar_btn')
        user_input.send_keys(user)
        pwd_input.send_keys(password)
        btn.click()
        time.sleep(1)
        self.success = self.browser.find_element_by_css_selector('.geetest_success_radar_tip')  # 獲取顯示結果的標簽

    def is_success(self):
        time.sleep(1)
        if self.success.text == "驗證成功":
            return True
        else:
            return False

    def get_position(self, img_label):
        location = img_label.location
        size = img_label.size
        top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
            'width']
        return (left, top, right, bottom)

    def get_screenshot(self):
        screenshot = self.browser.get_screenshot_as_png()
        f = BytesIO()
        f.write(screenshot)
        return Image.open(f)

    def get_position_scale(self, screen_shot):
        height = self.browser.execute_script('return document.documentElement.clientHeight')
        width = self.browser.execute_script('return document.documentElement.clientWidth')
        x_scale = screen_shot.size[0] / (width + 10)
        y_scale = screen_shot.size[1] / (height)
        return (x_scale, y_scale)

    def get_slideimg_screenshot(self, screenshot, position, scale):
        x_scale, y_scale = scale
        position = [position[0] * x_scale, position[1] * y_scale, position[2] * x_scale, position[3] * y_scale]
        return screenshot.crop(position)

    def compare_pixel(self, img1, img2, x, y):
        pixel1 = img1.load()[x, y]
        pixel2 = img2.load()[x, y]
        threshold = 50
        if abs(pixel1[0] - pixel2[0]) <= threshold:
            if abs(pixel1[1] - pixel2[1]) <= threshold:
                if abs(pixel1[2] - pixel2[2]) <= threshold:
                    return True
        return False

    def compare(self, full_img, slice_img):
        left = 65
        for i in range(full_img.size[0]):
            for j in range(full_img.size[1]):
                if not self.compare_pixel(full_img, slice_img, i, j):
                    return i
        return left

    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 = 4
            else:
                # 加速度為負 3
                a = -3
            # 初速度 v0
            v0 = v
            # 當前速度 v = v0 + at
            v = v0 + a * t
            # 移動距離 x = v0t + 1/2 * a * t^2
            move = v0 * t + 1 / 2 * a * t * t
            # 當前位移
            current += move
            # 加入軌跡
            track.append(round(move))
        return track

    def move_to_gap(self, slider, tracks):
        """
        拖動滑塊到缺口處
        :param slider: 滑塊
        :param tracks: 軌跡
        :return:
        """
        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 is_slide(self):
        time.sleep(1)
        try:
            slie_img = self.browser.find_element_by_css_selector('canvas.geetest_canvas_slice')
            if slie_img:
                return slie_img
        except NoSuchElementException:
            return False

    def slide_code(self):
        slice_img_label = self.browser.find_element_by_css_selector('div.geetest_slicebg')  # 找到滑動圖片標簽
        self.browser.execute_script(
            "document.getElementsByClassName('geetest_canvas_slice')[0].style['display'] = 'none'")  # 將小塊隱藏
        full_img_label = self.browser.find_element_by_css_selector('canvas.geetest_canvas_fullbg')  # 原始圖片的標簽
        position = self.get_position(slice_img_label)  # 獲取滑動驗證圖片的位置
        screenshot = self.get_screenshot()  # 截取整個瀏覽器圖片
        position_scale = self.get_position_scale(screenshot)  # 獲取截取圖片寬高和瀏覽器寬高的比例
        slice_img = self.get_slideimg_screenshot(screenshot, position, position_scale)  # 截取有缺口的滑動驗證圖片

        self.browser.execute_script(
            "document.getElementsByClassName('geetest_canvas_fullbg')[0].style['display'] = 'block'")  # 顯示原圖
        screenshot = self.get_screenshot()  # 獲取整個瀏覽器圖片
        full_img = self.get_slideimg_screenshot(screenshot, position, position_scale)  # 截取原圖
        self.browser.execute_script(
            "document.getElementsByClassName('geetest_canvas_slice')[0].style['display'] = 'block'")  # 將小塊重新顯示
        left = self.compare(full_img, slice_img)  # 將原圖與有缺口圖片進行比對,獲得缺口的最左端的位置
        left = left / position_scale[0]  # 將該位置還原為瀏覽器中的位置

        slide_btn = self.browser.find_element_by_css_selector('.geetest_slider_button')  # 獲取滑動按鈕
        track = self.get_track(left)  # 獲取滑動的軌跡
        self.move_to_gap(slide_btn, track)  # 進行滑動
        time.sleep(2)

    def is_pick(self):
        try:
            pick_img = self.browser.find_element_by_css_selector('img.geetest_item_img')
            return pick_img
        except NoSuchElementException:
            return False

    def pick_code(self):
        time.sleep(1)
        pick_img_label = self.browser.find_element_by_css_selector('img.geetest_item_img') #獲取點觸圖片標簽
        src = pick_img_label.get_attribute('src')  #獲取點觸圖片鏈接
        img_content = requests.get(src).content  #獲取圖片二進制內容
        f = BytesIO()
        f.write(img_content)
        img0 = Image.open(f)  #將圖片以文件的形式打開,主要是為了獲取圖片的大小
        scale = [pick_img_label.size['width'] / img0.size[0], pick_img_label.size['height'] / img0.size[1]] #獲取圖片與瀏覽器該標簽大小的比例
        cjy = chaojiying.Chaojiying_Client('*******', '******', '901489') #登錄超級鷹
        result = cjy.PostPic(img_content, '9005') #發送圖片並獲取結果
        if result['err_no'] == 0:  #對結果進行分析
            position = result['pic_str'].split('|')  # position = ['110,234','145,247','25,185']
            position = [[int(j) for j in i.split(',')] for i in position]  # position = [[110,234],[145,247],[25,185]]
            for items in position:  #模擬點擊
                ActionChains(self.browser).move_to_element_with_offset(pick_img_label, items[0] * scale[0],
                                                                       items[1] * scale[1]).click().perform()
                time.sleep(1)
            certern_btn = self.browser.find_element_by_css_selector('div.geetest_commit_tip')
            certern_btn.click()
        return cjy,result


if __name__ == '__main__':

    verifycode = Verifycode()
    verifycode.get_url('https://gtaccount.geetest.com/', '11', '11')
    if verifycode.is_success():
        print('success')
    elif verifycode.is_slide():
        verifycode.slide_code()
        if verifycode.is_success():
            print('slide success')
        else:
            print('slide failure')
    elif verifycode.is_pick():
        cjy,result = verifycode.pick_code()
        if verifycode.is_success():
            print('pick click success')
        else:
            print('pick click failure')
            if result['err_no'] == 0:
                r = cjy.ReportError(result['pic_id'])
                print(r)

 




 


免責聲明!

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



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