驗證碼

極驗滑動驗證碼缺口識別

極驗滑動驗證碼缺口識別

 

感謝崔大佬的《python3網絡爬蟲開發實戰》這本書讓我開了眼界。最近學習到驗證碼識別這一塊,發現隨着網站防爬手段的不斷更新,要填的坑真是越來越多,這篇文章就分享一下近段時間的填坑之旅。

首先來說一下書上的缺口識別函數,大概原理是通過兩張圖片的像素對比,識別像素不同的點確定缺口位置。然而這種識別方法的前提條件是必須要有兩張圖,一張圖是不含缺口的圖片,另一張則是帶缺口的圖片。

但由於時間原因,我接觸書上的代碼時,極驗證網站的滑動識別機制已經更新。對的,網站上截不到第一張不帶缺口的圖片了,所以在我手動copy崔大佬的代碼運行時結果必然是失敗的。

所以我自己寫了兩種解決滑動驗證碼的方法與大家分享,希望能擦出思想的火花。

第一種方法大概不算識別方法吧,因爲裏面有些投機取巧的地方。

投機就投機在我偶然發現了繞過驗證碼可以直接登錄的地方。

偶然間發現極驗證那個智能驗證按鈕,按鈕左邊的小圓圈裏的指針,會隨着鼠標的座標而轉動,然後我就發現這個小圈圈其實就是所謂智能驗證的核心。也就是只要在這個按鈕加載出來後,有鼠標在周圍移動時,就會被視爲是人爲操作;而如果是按鈕加載出來後代碼直接點擊就會被視爲爬蟲,然後就會有滑動驗證碼來作爲反爬蟲的一道大坎。

所以要解決這個問題其實很簡單,我能想到的就是用代碼在按鈕附近移動鼠標,巧妙地繞過驗證。

所以我導入了py的pyautogui 模塊,在確認了按鈕座標後根據座標位置讓鼠標在其周圍稍微移動。

核心代碼是:pyautogui.moveTo(x,y,duration=0.1)

pyautogui.moveTo(x+20,y+20,duration=5)

其中x,y爲按鈕的座標,duration爲移動延時。延時儘量調整在5秒左右,因爲移動速度過快那個小圈圈不買賬。

上面代碼執行後再點擊按鈕就會出現如下情況(嘿嘿):

第一種解決方法真的是絕妙,繞過了複雜的缺口識別,輕而易舉就完事了。

但,這似乎有點勝之不武,是男人就應該正面解決問題(鋼),在別人背後偷偷插一刀不是男人的作風。所以,重點來了,第二種方法就是正面解決問題的正路!

先講講思路吧。

在經過一番觀察後,我發現我想要找到那個缺口的位置似乎是黑乎乎的,這個黑,就成爲了解決問題的切入點。

是的,基本思路就是通過像素的rgb值找出那塊黑乎乎的目標位置的x座標。

然後我展開了我的一系列仔細的觀察,還能發現,那個滑塊的周圍的顏色似乎是固定的,

固定的橙色系(瞎說)。

下面是研究對象:

 

然後我就開始尋找這個橙色系的rgb值的浮動範圍。

下面是經過一系列計算後的結果:

上面的計算包括rgb三個值的浮動範圍,三個值兩兩相減的絕對值的浮動範圍以及rgb值的比率。

後來我就把這個橙色系的rgb範圍大概計算出來了:

r區間:[159,247]

g區間:[154,249]

b區間:[102,231]

abs(r-g)區間:[0,10]

abs(g-b)區間:[18,117]

計算三個值的比例我是通過rgb各值除以r值,然後取的g值和b值之和,浮動區間爲:

[1.4,1.9]

有了這些數據之後就很容易能識別出滑塊周圍的那一圈橙色系(似乎不是很專業,嘿嘿)。

接下來就是發現滑塊的位置的y座標的那條直線必定經過缺口。

能夠定位滑塊邊緣座標之後,爲了減少其他因素的影響,通過減少識別範圍提高識別率,對上面的圖進行範圍縮小處理(截圖)。

接下來要處理的就是如何識別出上面紅框框裏面的缺口的x座標。

黑是識別的關鍵,也就是上面紅線標註旁邊的那條豎線像素rgb三值之和是圖像中以x座標爲直線所有像素點rgb三個值和的最小值。

解決方法來了,也就是以x座標劃豎線,遍歷計算出rgb三值之和最小的那條,然後確定x座標,x座標即爲缺口位置。

 

如下爲截取的圖片:

 

 

在ps上放大分析:

 

 

大功告成!!!

from io import BytesIO
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
url = 'https://account.geetest.com/login'
EMAIL = '*********'
PASSWORD = '*********'
def open_firefox(url,email,password):
    '''

    :param url: 極驗證登錄頁面地址
    :param email: 登錄賬號
    :param password: 密碼
    :return:
    '''
    browers=webdriver.Firefox()
    browers.get(url)
    wait=WebDriverWait(browers,10)
    email_block=wait.until(EC.presence_of_element_located((By.ID, 'email')))
    password_block=wait.until((EC.presence_of_element_located((By.ID, 'password'))))
    email_block.send_keys(email)
    password_block.send_keys(password)
    button =wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))
    button.click()
    return browers,wait
def identify_gap(browers,wait):
    '''

    :param browers: 瀏覽器對象
    :param wait: wait對象
    :return: 缺口位置x座標
    '''
    #定位驗證碼圖片
    small_img=wait.until(EC.presence_of_element_located((By.XPATH, '//canvas[@class="geetest_canvas_bg geetest_absolute"]')))
    location=small_img.location  #獲取圖片位置,及大小
    size=small_img.size
    top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
            'width']    #確定截圖位置
    time.sleep(5)
    screenshot=browers.get_screenshot_as_png()
    screenshot = Image.open(BytesIO(screenshot))

    captcha = screenshot.crop((left, top, right, bottom))
    captcha.save(r'first.png')  #保存圖片
    first=Image.open(r'first.png')
    xsize,ysize=first.size
    pool=[]        #保存符合條件的像素點的座標信息的數據池
    pix=first.load()
    for i in range(xsize):            #顏色識別區域
        for j in range(ysize):
            if 159<=(pix[i,j])[0]<=247 and 154<=(pix[i,j])[1] <=249 and 102 <=(pix[i,j])[2]<=231:
                if 0<=abs((pix[i,j])[0]-(pix[i,j])[1])<=10:
                    if  18<=abs((pix[i,j])[1]-(pix[i,j])[2])<=117  :
                        if 1.4<=(pix[i,j])[1]/(pix[i,j])[0]+(pix[i,j])[2]/(pix[i,j])[0]<=1.97:
                            pool.append((i,j))
    #print(pool)
    x,y=(pool[0])[0],(pool[0])[1]     #獲取第一個符合條件的像素點的位置
    captcha1=screenshot.crop((left+x,top+y,right,top+y+5))    #進行截圖
    captcha1.save(r'second.png')
    Pool=[]         # 第二張截圖根據x座標的每一條豎線的rgb值和的數據池
    second=Image.open(r'second.png')
    Xsize,Ysize=second.size
    pix1=second.load()
    for i in range(Xsize):
        sum=0
        for j in range(Ysize):
             sum+=(pix1[i,j])[0]+(pix1[i,j])[1]+(pix1[i,j])[1]
        Pool.append((i,sum))
    Pool=sorted(Pool,key=lambda x:x[1])      #排序,找出rgb值得和的最低豎線的x座標
    #print(Pool)
    return (Pool[0])[0]+x   #返回偏移值



if __name__=='__main__':
    browers,wait=open_firefox(url=url,email=EMAIL,password=PASSWORD)

    print(identify_gap(browers,wait))

轉自:https://zhuanlan.zhihu.com/p/52705053

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