Python用KNN演算法實現驗證碼識別

作為一名爬蟲愛好者,把互聯網作為資料庫的同時總會遇到很多坑,其中一個就是驗證碼,設置驗證碼一大理由就是為了限制你亂爬,不過對於很多簡單的驗證碼,破解還是相當容易的。比如類似下面這種。

最簡單的辦法是直接用pytesseract庫,具體方法請自行搜索。當然如果不想用它,自己來寫一個識別演算法也並不難。可以用機器學習裡面比較基礎的KNN演算法。

先來介紹一下這個KNN演算法,他全稱叫K最近鄰(kNN,k-NearestNeighbor),所謂K最近鄰,就是離誰最近,是誰的可能性就更大。什麼意思,舉個例子就明白了。

我現在已經統計了一組手機數據,方便起見,假設只有高端機和低端機兩個分類,如下:

此時如果有一個新手機T(1500元,15小時),該怎麼判斷是什麼手機呢?我把它們放到同一個坐標系中去比較一下。

它並不能很好地跟已知數據完全重合,但不要緊,可以算一下它與其他手機的距離。經過計算,與T的距離D<E<C<B<A。這5個距離裡面我們選擇K個來作為判定標準,比如選擇3個,因此最接近的就是C、D、E。這三個裡面有兩個低端機,一個高端機,所以我們判斷,T是一個低端機。

就是這麼簡單。當然了,這裡面只是舉個例子所以數據量比較小,當數據量足夠大時,比如有200台低端機和300台高端機,選出最接近的200個數據,其中哪種機器多就歸到哪種,得出的結論就會比較準確。

這就是最簡單的KNN演算法,離哪個近,就把它歸類為哪個種類。這是二維,還可以再加個「像素」屬性,變為三維,仍然可以求最近距離。拓展到N個屬性也是一樣的道理。

基本原理就是這樣,下面來介紹如何識別驗證碼,以下代碼基於Python3

還是這張驗證碼,我們把它放大一點可以看到,其實他就是一個一個的像素點構成的。

裡面顏色太多不好操作,首先把這張圖變成黑白的

from PIL import Image#引入Image類im = Image.open("圖片路徑")#打開圖片im=im.convert("1")#原來圖片是彩色的,這裡把圖片轉換成模式"1",即黑白的im.show()#顯示圖片

於是我們就得到了這麼一張圖

可以把每個點的顏色轉變成0和1,並列印出來

for i in range(im.size[1]): temp=[] for j in range(im.size[0]): pos=(j,i) col=im.getpixel(pos)#獲取某坐標的點的顏色,黑色為0,白色為255,為了顯示規程,把它轉變成1了 if col==255: col=1 temp.append(col) print(temp)

可以從列印出來的字元中隱約看到這幾個數字

然後處理一下這串字元,把不連續的點,即上下左右都沒有同色的點去掉

im2 = im for i in range(im.size[1]): for j in range(im.size[0]): #pos=(j,i) #print(pos) if i==0 or j==0 or i==im.size[1]-1 or j==im.size[0]-1: im2.putpixel((j,i),255)#圖片最外面一周都換成白點 elif im.getpixel((j,i))!=im.getpixel((j-1,i)) and im.getpixel((j,i))!=im.getpixel((j+1,i)) and im.getpixel((j,i-1))!=im.getpixel((j,i)) and im.getpixel((j,i+1))!=im.getpixel((j,i)): im2.putpixel((j,i),255)#不連續點也都換成白點

然後開始切割圖片

inletter = False foundletter = False start = 0 end = 0 letters = []#用來記錄每個數字的起始位置 for x in range(im2.size[0]): for y in range(im2.size[1]): pix = im2.getpixel((x, y)) if pix != 255: inletter = True#如果不是白色,這說明已經開始接觸到數字了 if foundletter == False and inletter == True: foundletter = True start = x#數字的起始坐標 if foundletter == True and inletter == False: foundletter = False end = x-1#數字的結束位置 letters.append((start, end)) inletter = False

letters的值為[(7,14), (20, 27), (33, 40), (46, 54)],就是每個數字的起始X坐標,同樣道理獲得每個數字的起始Y坐標,然後切割開來。

region = (起始X,起始Y,結束X,結束Y) #裁切圖片 im3 = im2.crop(region)#im3就是這個區域內的圖形,也就是提取的數字 #im3.show()

於是得出了四個切割好的數字

這個時候怎麼做呢,我們把第一個數字9用0和1來列印看看。

[1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0,0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1,1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1,1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,0, 1, 1, 0, 0, 1, 0, 1]

總大小7*14,總共98個點

到這裡差不多就出來了,把每個點看成一個屬性,問題就轉化成了:已知一個數有98個屬性,也知道他的種類是9,同樣,還有很多很多這樣的數,知道屬性+種類。然後要做的,就是拿N個樣本作為標準集合,有新的數要識別的時候,選出最接近的K個,這K個裡面,數量最多的就是這個數的值。


推薦閱讀:

如何在瀏覽器中高效抓包分析數據?
Scrapy爬圖片(一)
未來的旅遊的熱點是什麼?旅遊點評數據分析
從零開始寫Python爬蟲 --- 2.6 爬蟲實踐:重構排行榜小說爬蟲&Mysql資料庫
從零開始寫Python爬蟲 --- 爬蟲實踐:螺紋鋼數據&Cookies

TAG:Python | 爬虫 | 机器学习 |