在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')
原文发布在 自动化软件测试 微信公众号,欢迎关注
原文地址:https://mp.weixin.qq.com/s/x3QT8njMX2wKPXKxqDPRyg