【2022 年】Python3 爬蟲教程 - OpenCV 圖像匹配識別滑動驗證碼缺口(2022年高考時間)
上一節我們學習了利用 OCR 技術對圖形驗證碼進行識別的方法,但隨著互聯網技術的發展,各種新型驗證碼層出不窮,最具有代表性的便是滑動驗證碼了。

本節我們首先介紹下滑動驗證碼的驗證流程,然后介紹一個簡易的利用圖像處理技術來識別滑動驗證碼缺口的方法。
1. 滑動驗證碼
說起滑動驗證碼,比較有代表性的服務商有極驗、網易易盾等,驗證碼效果如圖所示:
驗證碼下方通常會有一個滑軌,同時帶有文字提示「拖動滑塊完成拼圖」,我們需要按住滑軌上的滑塊向右拖拽,這時候驗證碼最左側的滑塊便會跟隨滑軌上的滑塊向右移動,在驗證碼右側會有一個滑塊缺口,我們需要恰好將滑塊拖動到目標缺口處,這時候就算驗證成功了,驗證成功的效果如圖所示:
所以,如果我們想要用爬蟲來自動化完成這一流程的話,關鍵步驟有如下兩個:
識別出目標缺口的位置
將缺口滑動到對應位置
其中第二步的實現有多種方式,比如我們可以用 Selenium 等自動化工具模擬完成這個流程,驗證并登錄成功之后獲取對應的 Cookies 或 Token 等信息再進行后續的操作,但這種方法運行效率會比較低。另一種方法便是直接逆向驗證碼背后的 JavaScript 邏輯,將缺口信息直接傳給 JavaScript 代碼執行獲取一些類似 “密鑰” 的信息,再利用這些 “密鑰” 進行下一步的操作。
注意:由于某些出于安全考慮的原因,本書不會再介紹第二步的具體操作,而是只針對于第一步的技術問題進行講解。
因此,本節只會針對于第一步即如何識別出目標缺口的位置進行介紹,即給定一張驗證碼圖片,如何用圖像識別的方法識別出缺口的位置。
2. 基本原理
本節我們會介紹利用 OpenCV 進行缺口識別的方法,輸入一張帶有缺口的驗證碼圖片,輸出缺口的位置(一般為缺口左側橫坐標)。
比如輸入的驗證碼圖片如下:
最后輸出的識別結果如下:
本節介紹的方法是利用 OpenCV 進行基本的圖像處理來實現的,主要步驟包括:
對驗證碼圖片進行高斯模糊濾波處理,消除部分噪聲干擾
對驗證碼圖片應用邊緣檢測算法,通過調整相應閾值識別出滑塊邊緣
對上一步得到的各個邊緣輪廓信息,通過對比面積、位置、周長等特征篩選出最可能的輪廓位置,得到缺口位置。
3. 準備工作
在本節開始之前請確保已經安裝好了 python-opencv 庫,安裝方式如下:
1
pip3 install python-opencv
如果安裝出現問題,可以參考詳細的安裝步驟:https://setup.scrape.center/python-opencv。
另外建議提前準備一張滑動驗證碼圖片,樣例圖片-:https://github.com/Python3WebSpider/CrackSlideCaptcha/blob/cv/captcha.png,當然也可以從 https://captcha1.scrape.center/ 自行截取,最終的圖片如上文所示。
4. 基礎知識
在真正開始介紹之前,我們先需要了解一些 OpenCV 的基礎 API,以幫助我們更好地理解整個原理。
高斯濾波
高斯濾波是用來去除圖像中的一些噪聲的,基本效果其實就是把一張圖像變得模糊化,減少一些圖像噪聲干擾,從而為下一步的邊緣檢測做好鋪墊。
OpenCV 提供了一個用于實現高斯模糊的方法,叫做 GaussianBlur,方法聲明如下:
1
def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
比較重要的參數介紹如下:
src:即需要被處理的圖像。
ksize:進行高斯濾波處理所用的高斯內核大小,它需要是一個元組,包含 x 和 y 兩個維度。
sigmaX:表示高斯核函數在 X 方向的的標準偏差。
sigmaY:表示高斯核函數在 Y 方向的的標準偏差,若 sigmaY 為 0,就將它設為 sigmaX,如果 sigmaX 和 sigmaY 都是 0,那么 sigmaX 和 sigmaY 就通過 ksize 計算得出。
這里 ksize 和 sigmaX 是必傳參數,對本節樣例圖片,ksize 我們可以取 (5, 5),sigmaX 可以取 0。
經過高斯濾波處理后,圖像會變得模糊,效果如下:
邊緣檢測
由于驗證碼目標缺口通常具有比較明顯的邊緣,所以借助于一些邊緣檢測算法并通過調整閾值是可以找出它的位置的。目前應用比較廣泛的邊緣檢測算法是 Canny,它是 John F. Canny 于 1986 年開發出來的一個多級邊緣檢測算法,效果還是不錯的,OpenCV 也對此算法進行了實現,方法名稱就叫做 Canny,聲明如下:
1
def Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None)
比較重要的參數介紹如下:
image:即需要被處理的圖像。
threshold1、threshold2:兩個閾值,分別為最小和最大判定臨界點。
apertureSize:用于查找圖像漸變的 Sobel 內核的大小。
L2gradient:指定用于查找梯度幅度的等式。
通常來說,我們只需要設定 threshold1 和 threshold2 即可,其數值大小需要視不同圖像而定,比如本節樣例圖片可以分別取 200 和 450。
經過邊緣檢測算法處理后,一些比較明顯的邊緣信息會被保留下來,效果如下:
輪廓提取
進行邊緣檢測處理后,我們可以看到圖像中會保留有比較明顯的邊緣信息,下一步我們可以用 OpenCV 將邊緣輪廓提取出來,這里需要用到 findContours 方法,方法聲明如下:
1
def findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
比較重要的參數介紹如下:
image:即需要被處理的圖像。
mode:定義輪廓的檢索模式,詳情見 OpenCV 的 RetrievalModes 的介紹。
method:定義輪廓的近似方法,詳情見 OpenCV 的 ContourApproximationModes 的介紹。
在這里,我們選取 mode 為 RETR_CCOMP,method 為 CHAIN_APPROX_SIMPLE,具體的選型標準可以參考 OpenCV 的文檔介紹,這里不再展開講解。
外接矩形
提取到輪廓之后,為了方便進行判定,我們可以將輪廓的外界矩形計算出來,這樣方便我們根據面積、位置、周長等參數進行判定,以得出該輪廓是不是目標滑塊的輪廓。
計算外接矩形使用的方法是 boundingRect,方法聲明如下:
1
def boundingRect(array)
只有一個參數:
array:可以是一個灰度圖或者 2D 點集,這里可以傳入輪廓信息。
經過輪廓信息和外接矩形判定之后,我們可以得到類似如下結果:
可以看到這樣就能成功獲取各個輪廓的外接矩形,接下來我們根據外接矩形的面積、和位置就能篩選出缺口對應的位置了。
輪廓面積
現在已經得到了各個外接矩形,但是很明顯有些矩形不是我們想要的,我們可以根據面積、周長等來進行篩選,這里就需要用到計算面積的方法,叫做 contourArea,方法定義如下:
1
def contourArea(contour, oriented=None)
參數介紹如下:
contour:輪廓信息。
oriented:面向區域標識符。有默認值 False。若為 True,該函數返回一個帶符號的面積值,正負取決于輪廓的方向(順時針還是逆時針)。若為 False,表示以絕對值返回。
返回結果就是輪廓的面積。
輪廓周長
同樣,周長的計算也有對應的方法,叫做 arcLength,方法定義如下:
1
def arcLength(curve, closed)
參數介紹如下:
curve:輪廓信息。
closed:表示輪廓是否封閉。
返回結果就是輪廓的周長。
以上內容介紹了一些 OpenCV 內置方法,了解了這些方法的用法,我們可以對下文的具體實現有更透徹的理解。
5. 缺口識別
接下來我們就開始真正實現一下缺口識別算法了。
首先我們定義高斯濾波、邊緣檢測、輪廓提取的三個方法,實現如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import cv2
GAUSSIAN_BLUR_KERNEL_SIZE = (5, 5)
GAUSSIAN_BLUR_SIGMA_X = 0
CANNY_THRESHOLD1 = 200
CANNY_THRESHOLD2 = 450
def get_gaussian_blur_image(image):
return cv2.GaussianBlur(image, GAUSSIAN_BLUR_KERNEL_SIZE, GAUSSIAN_BLUR_SIGMA_X)
def get_canny_image(image):
return cv2.Canny(image, CANNY_THRESHOLD1, CANNY_THRESHOLD2)
def get_contours(image):
contours, _ = cv2.findContours(image, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
return contours
三個方法介紹如下:
get_gaussian_blur_image:傳入待處理圖像信息,返回高斯濾波處理后的圖像,ksize 定義為 (5, 5),sigmaX 定義為 0。
get_canny_image:傳入待處理圖像信息,返回邊緣檢測處理后的圖像,threshold1 和 threshold2 分別定義為 200 和 450。
get_contours:傳入待處理圖像信息,返回檢測到的輪廓信息,這里 mode 設定為 RETR_CCOMP,method 設定為 CHAIN_APPROX_SIMPLE。
原始待識別驗證碼命名為 captcha.png,接下來我們分別調用以上方法對驗證碼進行處理:
1
2
3
4
5
image_raw = cv2.imread('captcha.png')
image_height, image_width, _ = image_raw.shape
image_gaussian_blur = get_gaussian_blur_image(image_raw)
image_canny = get_canny_image(image_gaussian_blur)
contours = get_contours(image_canny)
原始圖片我們命名為 image_raw 變量,讀取圖片之后獲取其寬高像素信息,接著調用了 get_gaussian_blur_image 方法進行高斯濾波處理,返回結果命名為 image_gaussian_blur,接著將 image_gaussian_blur 傳給 get_canny_image 方法進行邊緣檢測處理,返回結果命名為 image_canny,接著調用 get_contours 方法得到各個邊緣的輪廓信息,賦值為 contours 變量。
好,得到各個輪廓信息之后,我們便需要根據各個輪廓的外接矩形的面積、周長、位置來篩選我們想要結果了。
所以,我們需要先確定怎么來篩選,比如面積我們可以設定一個范圍,周長設定一個范圍,缺口位置設定一個范圍,通過實際測量,我們可以得出目標缺口的外接矩形的高度大約是驗證碼高度的 0.25 倍,寬度大約是驗證碼寬度的 0.15 倍。在允許誤差 20% 的情況下,根據驗證碼的寬高信息我們大約可以計算出面積、周長的范圍,同時缺口位置(缺口左側)也有一個最小偏移值,比如最小偏移是驗證碼寬度的 0.2 倍,最大偏移是驗證碼寬度的 0.85 倍。綜合這些內容,我們可以定義三個閾值方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_contour_area_threshold(image_width, image_height):
contour_area_min = (image_width * 0.15) * (image_height * 0.25) * 0.8
contour_area_max = (image_width * 0.15) * (image_height * 0.25) * 1.2
return contour_area_min, contour_area_max
def get_arc_length_threshold(image_width, image_height):
arc_length_min = ((image_width * 0.15) + (image_height * 0.25)) * 2 * 0.8
arc_length_max = ((image_width * 0.15) + (image_height * 0.25)) * 2 * 1.2
return arc_length_min, arc_length_max
def get_offset_threshold(image_width):
offset_min = 0.2 * image_width
offset_max = 0.85 * image_width
return offset_min, offset_max
三個方法介紹如下:
get_contour_area_threshold:定義目標輪廓的下限和上限面積,分別為 contour_area_min 和 contour_area_max。
get_arc_length_threshold:定義目標輪廓的下限和上限周長,分別為 arc_length_min 和 arc_length_max。
get_offset_threshold:定義目標輪廓左側的下限和上限偏移量,分別為 offset_min 和 offset_max。
最后我們只需要遍歷各個輪廓信息,根據上述限定條件進行篩選,最后得出目標輪廓信息即可,實現如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
contour_area_min, contour_area_max = get_contour_area_threshold(image_width, image_height)
arc_length_min, arc_length_max = get_arc_length_threshold(image_width, image_height)
offset_min, offset_max = get_offset_threshold(image_width)
offset = None
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
if contour_area_min < cv2.contourArea(contour) < contour_area_max and \
arc_length_min < cv2.arcLength(contour, True) < arc_length_max and \
offset_min < x < offset_max:
cv2.rectangle(image_raw, (x, y), (x + w, y + h), (0, 0, 255), 2)
offset = x
cv2.imwrite('image_label.png', image_raw)
print('offset', offset)
這里我們首先調用了 get_contour_area_threshold、get_arc_length_threshold、get_offset_threshold 方法獲取了輪廓的判定閾值,然后遍歷了 contours 根據這些閾值進行了篩選,最終得到的外接矩形的 x 值就是目標缺口的偏移量。
同時目標缺口的外接矩形我們也調用了 rectangle 方法進行了標注,最終將其保存為 image_label.png 圖像。
最終運行結果如下:
1
offset 163
同時得到輸出的 image_label.png 文件如下:
這樣我們就成功提取出來了目標滑塊的位置了,本節的問題得以解決。
注意:出于安全考慮,本書只針對于第一步 - 識別驗證碼缺口位置的的技術問題進行講解,關于怎樣去模擬滑動或者繞過驗證碼,本書不再進行介紹,可以自行搜索相關資料探索。
6. 總結
本節我們介紹了利用 OpenCV 來識別滑動驗證碼缺口的方法,其中涉及到了一些關鍵的圖像處理和識別技術,如高斯模糊、邊緣檢測、輪廓提取等算法。了解了基本的圖像識別技術后,我們可以舉一反三,將其應用到其他類型的工作上,也會很有幫助。
本節代碼:https://github.com/Python3WebSpider/CrackSlideCaptcha/tree/cv,注意這里是 cv 分支。
OpenCV Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。