在web頁面中,經常會遇到驗證碼,這對于我這麼一個熱愛web自動化測試人員,就變成了一件頭疼的事。于是千方百計找各種資源得到破解簡單的驗證碼方法。
識别驗證碼
大緻分如下幾個步驟:
1.擷取驗證碼圖檔
2.灰階處理
3.增加對比度
4.降噪
5.識别
>>>>擷取驗證碼
通過各種方法,将含有驗證碼的圖檔擷取并存貯在本地。
本次的方法是:截取目前web頁面,然後擷取驗證碼在web頁面中的位置,通過位置定位驗證碼圖檔再次截取。
以163郵箱注冊頁面為例
用到的庫:selenium、PIL
如果是python2.x,pip install PIL;在python3.x中PIL被移植到pillow 中,是以導入時需要導入pillow,pip install pillow
1 from PIL import Image
2
3 import time
4 from selenium import webdriver
5
6
7
8 def get_code_img(driver):
9
10 time.sleep(1)
11
12 # 截取整個浏覽器圖
13 driver.save_screenshot('webImg.png')
14
15 # 擷取code元素坐标
16 code_element = driver.find_element_by_id('vcodeImg')
17
18 # 擷取code圖檔坐标值
19 left_location = code_element.location['x']
20 top_location = code_element.location['y']
21
22 right_location = code_element.size['width'] + left_location
23 below_location = code_element.size['height'] + top_location
24
25 # 通過坐标值得到code image圖
26 web_img = Image.open("webImg.png")
27 code_img = web_img.crop((left_location,top_location,right_location,below_location))
28 code_img.save("codeImg.png")
save_screenshot:webdriver中提供的一個方法,截取整個web頁面
code_element.location:擷取某個的位置
例如:print(code_element.location)的結果為:{'x': 632, 'y': 511}
他是以圖檔的左上角為基準點,向右為x,向下為y
code_element.size:擷取圖檔的尺寸
crop:是通過四個坐标點擷取位置截圖并且生成一張新圖,他是Image 中的一個方法。
運作代碼
1 if __name__ == '__main__':
2
3 base_url = 'http://reg.email.163.com/unireg/call.do?cmd=register.entrance&from=126mail'
4
5 driver = webdriver.Chrome()
6 driver.maximize_window()
7 driver.get(base_url)
8 get_code_img(driver)
9 driver.close()
運作後獲得兩張圖檔webImg.png和codeImg.png。codeImg如下:

>>>>灰階處理/增加對比色
将圖檔的顔色變成灰色并且增加對比色,識别時減少不必要的幹擾。
1 def gray_img(img):
2 code_img = Image.open(img)
3 # 轉換為灰階
4 gray_img = code_img.convert('L')
5 # 增強亮度
6 enhance_img = ImageEnhance.Contrast(gray_img)
7 enhance_img = enhance_img.enhance(3)
8 return enhance_img
9
10
11
12 if __name__ == '__main__':
13
14 gray_img('codeImg.png').show()
運作後結果
>>>>降噪
根據一個點A的RGB值,與周圍的4個點的RGB值進行比較,最初設定一個值N即判斷數量(0<N<4),當A的RGB值與周圍4個點的RGB相等數小于N時會被視為燥點,被消除。
1 def clear_noise(img):
2
3 noise_img = img.load()
4 # 擷取圖檔的尺寸
5 w,h = img.size
6
7 for y in range(1,h-1):
8 for x in range(1,w-1):
9 count = 0
10 if noise_img[x,y-1] > 245:
11 count = count + 1
12 if noise_img[x,y+1] > 245:
13 count = count + 1
14 if noise_img[x-1,y] > 245:
15 count = count + 1
16 if noise_img[x+1,y] > 245:
17 count = count + 1
18 if noise_img[x-1,y-1] > 245:
19 count = count + 1
20 if noise_img[x-1,y+1] > 245:
21 count = count + 1
22 if noise_img[x+1,y-1] > 245:
23 count = count + 1
24 if noise_img[x+1,y+1] > 245:
25 count = count + 1
26 if count > 4:
27 noise_img[x,y] = 255
28 return img
29
30 if __name__ == '__main__':
31 img = gray_img('codeImg.png')
32 clear_noise(img).show()
運作後結果
>>>>識别
識别使用的是pytesseract包。
Pytesseract包依賴于tesseract,安裝的時候兩個都需安裝
詳情參考:
tesseract: https://github.com/sirfz/tesserocr
pytesseract:https://github.com/madmaze/pytesseract
1 text = pytesseract.image_to_string(img)
2 print(text)
很遺憾,上面的圖沒有識别出來。
完整代碼運作識别
以下圖驗證碼為例
1 from PIL import Image, ImageEnhance
2 import time
3 import pytesseract
4 from selenium import webdriver
5
6
7 def clear_noise(img):
8 noise_img = img.load()
9 # 擷取圖檔的尺寸
10 w,h = img.size
11
12 for y in range(1,h-1):
13 for x in range(1,w-1):
14 count = 0
15 if noise_img[x,y-1] > 245:
16 count = count + 1
17 if noise_img[x,y+1] > 245:
18 count = count + 1
19 if noise_img[x-1,y] > 245:
20 count = count + 1
21 if noise_img[x+1,y] > 245:
22 count = count + 1
23 if noise_img[x-1,y-1] > 245:
24 count = count + 1
25 if noise_img[x-1,y+1] > 245:
26 count = count + 1
27 if noise_img[x+1,y-1] > 245:
28 count = count + 1
29 if noise_img[x+1,y+1] > 245:
30 count = count + 1
31 if count > 4:
32 noise_img[x,y] = 255
33 return img
34
35
36 def get_code_img(driver):
37
38 time.sleep(1)
39
40 # 截取整個浏覽器圖
41 driver.save_screenshot('webImg.png')
42
43 # 擷取code元素坐标
44 code_element = driver.find_element_by_id('vcodeImg')
45
46 # 擷取code圖檔坐标值
47 left_location = code_element.location['x']
48 top_location = code_element.location['y']
49
50 right_location = code_element.size['width'] + left_location
51 below_location = code_element.size['height'] + top_location
52
53 # 通過坐标值得到code image圖
54 web_img = Image.open("webImg.png")
55 code_img = web_img.crop((left_location,top_location,right_location,below_location))
56 code_img.save("codeImg.png")
57
58
59 def gray_img(img):
60 code_img = Image.open(img)
61 # 轉換為灰階
62 gray_img = code_img.convert('L')
63 # 增強亮度
64 enhance_img = ImageEnhance.Contrast(gray_img)
65 enhance_img = enhance_img.enhance(3)
66 return enhance_img
67
68
69 if __name__ == '__main__':
70
71 # base_url = 'http://reg.email.163.com/unireg/call.do?cmd=register.entrance&from=126mail'
72 #
73 # driver = webdriver.Chrome()
74 # driver.maximize_window()
75 # driver.get(base_url)
76 # get_code_img(driver)
77 # driver.close()
78 img = gray_img('d.png')
79 img = clear_noise(img)
80 img.show()
81 text = pytesseract.image_to_string(img)
82 print(text)
運作結果
雖然還是失敗的。但至少已經接近了...
此次隻是對驗證碼的識别做簡單的嘗試。雖然此方法識别率不是很高。當然網上有很多收費的識别平台,通過大量聯系識别率是很高的,有興趣的可以去了解下。
在認識驗證碼後我又來興趣了,想去探個究竟驗證碼是怎樣生成的...下次分享(皮一下)
python之驗證碼的生成
在識别驗證碼的玩虐後,決定去看看他是怎麼生成的。
大緻步驟:
1.建立圖檔
2.對背景像素處理
3.寫入識别碼
4.增加幹擾線
5.濾鏡處理
用到的庫
1 import random
2
3 from PIL import Image, ImageFont, ImageDraw,ImageFilter
在開始之前,了解下Image下圖檔的基本屬性
print(Image.open('img.jpeg'))
結果:<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=500x291 at 0x103BA3FD0>
列印的是:圖檔格式、mode:彩色值、size:尺寸
也可以直接擷取該圖檔的相關屬性
img = Image.open('img.jpeg')
print(img.size, img.format, img.mode)
結果: (500, 291) JPEG RGB
現在開始生成驗證碼
>>>>建立圖檔
1 from PIL import Image
2
3 width = 240
4 height = 60
5
6 # 圖像生成
7 image = Image.new('RGB', (width,height), color='red')
8 image.show()
new()是建立一個圖檔,第一個參數為圖檔mode也就是色彩值;
第二個參數為圖檔的大小;
第三個參數是圖檔顔色。
show()方法是展示圖檔
>>>>對背景像素處理
1 # 填充每個像素點
2 for i in range(width):
3 for j in range(height
4
5 ):
6 draw.point((i,j), fill=random_bgcolor())
random_bgcolor():也是自定義的方法,随機産生顔色。
def random_bgcolor():
return (random.randint(60,200), random.randint(60,200),random.randint(60,200))
傳回一個RGB色彩值,其中的顔色取值根據需要設定吧。
列印結果
>>>>寫入識别碼
1 draw = ImageDraw.Draw(image)
2 # 寫入資訊
3 for i in range(4):
4 draw.text((60*i+10, 10), get_random(1,4), font=font, fill=random_color())
ImageDraw.Draw(image)是在圖檔image上建立一個畫筆
For循環:循環産生4個數字或字母
draw.text()方法是寫入的内容,
第一個參數是坐标,坐标自己通過圖檔尺寸稍為計算下,合理布局;
第二個參數是寫入的内容值,這裡傳入的是讓系統随機産生一個數,方法可以自己定義;
第三個font為字型,設定的字型必須存在
第四個是對寫入的内容附上顔色,這裡傳入的是讓系統随機産生一個顔色,方法可以自己定義;
第二個參數的方法如下:
1 def get_random(num,many):
2 for i in range(many):
3 s = ""
4 for j in range(num):
5 n = random.randint(1,2) # n==1生成數字,n=2生成字母
6 if n == 1:
7 num1 = random.randint(0, 9)
8 s +=str(num1)
9 else:
10 s +=str(random.choice(string.ascii_letters))
11
12 return s
第三個參數字型:
font = ImageFont.truetype('Arial.ttf',36)
第四個參數的方法如下:
直接傳回RGB顔色值
1 def random_color():
2 return (random.randint(64,255), random.randint(64,255), random.randint(64,255))
運作上面代碼結果:
>>>>增加幹擾線
在生成的驗證碼圖檔上添加一條幹擾線
1 for i in range(2):
2 x1 = random.randint(0, width)
3 y1 = random.randint(0, height)
4 x2 = random.randint(0, width)
5 y2 = random.randint(0, height)
6 draw.line((x1, y1, x2, y2), fill=random_bgcolor(),width=3)
draw.line()是畫線方法
第一個參數:線條坐标,即位置。如上是在圖檔範圍内位置随機
第二個參數:線條的顔色,還是讓随機産生
第三個參數:線條的寬度,不設定的話預設為0
>>>>濾鏡處理
增加濾鏡,可以增加顔色的不同
很簡單,一行代碼搞定
1 image = image.filter(ImageFilter.BLUR)
結果如下:
非常抱歉,我設定産生的随機色顔色值沒有調對,導緻背景色和字型色顔色太接近,效果看起來不是很好。
但是濾鏡不是必須項,可以不設定。
完整代碼如下
1 import string
2
3 import random
4 from PIL import Image, ImageFont, ImageDraw,ImageFilter
5
6 # 生成随機大小數字
7 def get_random(num,many):
8 for i in range(many):
9 s = ""
10 for j in range(num):
11 n = random.randint(1,2) # n==1生成數字,n=2生成字母
12 if n == 1:
13 num1 = random.randint(0, 9)
14 s +=str(num1)
15 else:
16 s +=str(random.choice(string.ascii_letters))
17 return s
18
19 # 随機顔色RGB
20 def random_color():
21 return (random.randint(64,255), random.randint(64,255), random.randint(64,255))
22
23 # 随機顔色RGB
24 def random_bgcolor():
25 return (random.randint(60,200), random.randint(60,200), random.randint(60,200))
26
27 # 字型,字型大小
28 font = ImageFont.truetype('Arial.ttf',36)
29
30 # 圖檔尺寸
31 width = 240
32 height = 60
33
34 # 圖像生成
35 image = Image.new('RGB', (width,height), color='red')
36
37 # 建立繪圖對象
38 draw = ImageDraw.Draw(image)
39
40 # 填充背景色
41 for i in range(width):
42 for j in range(height):
43 draw.point((i,j), fill=random_bgcolor())
44
45 # 寫入資訊
46 for i in range(4):
47 draw.text((60*i+10, 10), get_random(1,4), font=font, fill=random_color())
48
49 # 插入幹擾線
50 for i in range(2):
51 x1 = random.randint(0, width)
52 y1 = random.randint(0, height)
53 x2 = random.randint(0, width)
54 y2 = random.randint(0, height)
55 draw.line((x1, y1, x2, y2), fill=random_bgcolor(),width=3)
56
57 # 添加濾鏡
58 image = image.filter(ImageFilter.BLUR)
59
60 # 展示圖檔
61 image.show()
62
63 # 儲存
64 image.save('code.png')