爬蟲驗證碼很難嗎?自動識別驗證碼程式瞭解一下?
首先,我對驗證碼做了初步的觀察分析。總結如下:
驗證碼中的字元位數始終為6位,並且是灰度影象;
字元之間的間隔看起來始終保持相同的間隔;
每個字元都是完全定義的;
影象有許多雜散的暗畫素,以及穿過影象的線條
我決定下載一個圖片驗證碼,並藉助 這款工具 以二進位制視覺化影象(0表示黑色,1表示白色畫素)。
我的觀察是正確的 – 影象尺寸為45×180,每個字元被分配一個30畫素的空間來擬合,從而使它們均勻間隔。
我將其包裝在一個迴圈中,寫了一個簡單的指令碼,從該站點獲取500個驗證碼影象,並將所有裁剪後的字元儲存到一個資料夾中。
第三次觀察 – 每個字元都有明確的定義。為了“清理”影象中的裁剪字元(刪除不必要的線和點),我使用了以下方法。
字元中的所有畫素都是純黑色(0)。我用了一個簡單的邏輯 – 如果它不是完全黑色的,就視為白色。因此,對於值大於0的每個畫素,將其重新分配為255。使用load()函式將影象轉換為45×180矩陣,然後對其進行處理。
pixel_matrix = cropped_image.load()
for col in range(0, cropped_image.height):
for row in range(0, cropped_image.width):
if pixel_matrix[row, col] != 0:
pixel_matrix[row, col] = 255
image.save("thresholded_image.png" )
為了更加清晰,我將程式碼應用至原始影象上。
原圖:
處理後:
可以看到處理後的影象中的非純黑畫素都已被移除,其中包括穿插影象的線條。
直到專案完成後,我才知道上述方法被稱為影象處理中的閾值處理。
第四次觀察 – 影象中有許多雜散畫素。
迴圈遍歷影象矩陣,如果相鄰畫素為白色,與相鄰畫素相對的畫素也為白色,且中心畫素為黑色的,則使中心畫素為白色。
for column in range(1, image.height - 1):
for row in range(1, image.width - 1):
if pixel_matrix[row, column] == 0 \
and pixel_matrix[row , column - 1] == 255 and pixel_matrix[row, column + 1] == 255 :
pixel_matrix[row, column] = 255
if pixel_matrix[row, column] == 0 \
and pixel_matrix[row - 1, column] == 255 and pixel_matrix[row + 1, column] == 255:
pixel_matrix[row, column] = 255
一個將按照字元排序的相似影象分組(約束條件:暗畫素數量,相似度>= 90 – 95 %)
一個從每個分組字元中獲得最佳影象
因此現在已生成了庫影象。將它們轉換為畫素矩陣,並將“點陣圖”儲存為JSON檔案。
最後,這是解決任何新的驗證碼影象的演算法。
使用相同的演算法減少新影象中不必要的干擾
對於新驗證碼影象中的每個字元,通過我生成的JSON點陣圖強制執行。根據對應的暗畫素匹配來計算相似度。
這意味著,如果一個畫素為暗畫素,在影象中的位置為(4,8),並且如果該畫素在我們的骨架影象/點陣圖中的相同位置處為暗畫素,則計數值會遞增1。
該計數與骨架影象中暗畫素的數量相比,用於計算百分比匹配。
選擇匹配率最高的字元。
import json
characters = "123456789abcdefghijklmnpqrstuvwxyz"
captcha = ""
with open("bitmaps.json", "r") as f:
bitmap = json.load(f)
for j in range(image.width/6, image.width + 1, image.width/6):
character_image = image.crop((j - 30, 12, j, 44))
character_matrix = character_image.load()
matches = {}
for char in characters:
match = 0
black = 0
bitmap_matrix = bitmap[char]
for y in range(0, 32):
for x in range(0, 30):
if character_matrix[x, y] == bitmap_matrix[y][x] and bitmap_matrix[y][x] == 0:
match += 1
if bitmap_matrix[y][x] == 0:
black += 1
perc = float(match) / float(black)
matches.update({perc: char[0].upper()})
try:
captcha += matches[max(matches.keys())]
except ValueError:
print("failed captcha")
captcha += "0"
print captcha
最終得到的結果如下:
可以看到驗證碼被成功識別為Z5M3MQ!
總結
這個專案對於我本人而言,也是一次非常好的學習經歷。我還開發了一個 Chrome外掛,目前有1800+的使用者,歡迎大家安裝使用!此外,如果你有任何的意見和建議也歡迎向我提出。以上的程式碼我已託管在GitHub,你可以 在這裡找到 。
歡迎加入我的千人交流答疑群:125240963