簡介
爬蟲在抓網站數據時,不可避免要和驗證碼做長久鬥爭。當然能繞過最好,但是總有繞不過的驗證碼,此時,對於簡單的可以嘗試破解,有難度的對接打碼平臺。現在驗證碼多種多樣,點選,滑動,英文字母組合等,接下來簡單的聊一聊英文字母組合中的這兩種驗證碼的破解。
流程
識別英文字母組合驗證碼的一般步驟通常是:加載圖片,灰度化,二值化,去除噪點(包括干擾線),字符分割,訓練模型,識別。其中的難點一般在去燥和字符分割這兩步,比如搜狗微信的驗證碼干擾線比字符還要粗,去燥不可避免會傷及字符,比如百度驗證碼粘連,扭曲,變形。這種驗證碼不需要去除噪點,單恰恰是特別難的,分割特別麻煩,當然要是你能生成各種類型的驗證碼,那驗證碼的破解就非常easy了,只需要將帶有標籤的大批量驗證碼通過神經網絡CNN訓練出模型,可以高精度識別。
驗證碼處理一般需要使用PIL,opencv這兩個模塊。這裏我們主要使用PIL庫,驗證碼的處理主要是對圖片的像素點進行操作,彩色圖像中的每個像素的顏色有R、G、B三個分量決定,而灰度圖像是R、G、B三個分量相同的一種特殊的彩色圖像,將圖片進行灰度化可以通過這兩種方式實現。
1:像素點的R、G、B三色求平均值
2:R * 0.3+ G * 0.59 +B * 0.11
二值化是通過閥值將像素點轉化成非白(255)即黑(0)的值
具體代碼實現:
# 加載圖片
img = Image.open('0.jpg')
# 圖片轉化爲灰色圖片
img = img.convert("L")
# 圖片灰度化
pixdata = img.load()
for y in range(img.size[1]):
for x in range(img.size[0]):
if x <= 5 or x >= 195 or y <= 5 or y >= 47:
pixdata[x, y] = 225
if pixdata[x, y] < 100:
pixdata[x, y] = 0
else:
pixdata[x, y] = 255
這兩步處理之後圖片變爲,
圖片去除噪點有很多種方法,濾波法,鄰域法,輪廓法等,這裏通過8鄰域法實現,主要是控制好閥值避免誤傷,當一次效果理想時,可以適當增加降噪次數。
def denoising(im):
"""圖片去除噪點"""
pixdata = im.load()
w, h = im.size
for j in range(1, h - 1):
for i in range(1, w - 1):
count = 0
l = pixdata[i, j]
if l == pixdata[i, j - 1]:
count = count + 1
if l == pixdata[i, j + 1]:
count = count + 1
if l == pixdata[i + 1, j - 1]:
count = count + 1
if l == pixdata[i + 1, j + 1]:
count = count + 1
if l == pixdata[i + 1, j]:
count = count + 1
if l == pixdata[i - 1, j + 1]:
count = count + 1
if l == pixdata[i - 1, j - 1]:
count = count + 1
if l == pixdata[i - 1, j]:
count = count + 1
if count < 4:
pixdata[i, j] = 255
return im
去燥之後的圖片已經基本不影響分割和識別了
驗證碼分割一樣有很多中針對特定情形的方法。圖形基本不粘連的垂直陰影法,cfs通道法,以及稍稍粘連使用滴水算法切割等
效果都特別好。博主對這種明顯不粘連的驗證碼才用的是垂直陰影法切割
# 垂直陰影法分割
def get_projection_x(image, invert=False):
p_x = [0 for x in range(image.size[0])]
for w in range(image.size[1]):
for h in range(image.size[0]):
if invert:
if image.getpixel((h, w)) >= 200:
p_x[h] += 1
continue
else:
if image.getpixel((h, w)) <= 5:
p_x[h] += 1
continue
# 判斷邊界
l, r = 0, 0
flag = False
cuts = []
for i, int in enumerate(p_x):
# 閾值這裏爲2
if flag is False and int > 3:
l = i
flag = True
if flag and int <= 3:
r = i - 1
flag = False
cuts.append((l, r))
return cuts
分割之後得到的單個字符,雖然祛噪點並不是特別完美,採用神經網絡或者支持向量機訓練模型並不大影響識別效果,但是打標籤的過程並不享受。