俗语说得好:良好的开端是成功的一半,在Web自动化测试中,首页登录是每个测试用例执行的前提。
首页登录有验证码,也有为了安全而设置的验证码,对这些验证码进行自动识别,提取文字进行登录,是自动化测试良好的开端,否则一切都无从谈起。
至于有些博客提到开发设置万能码等,如果开发团队支持也是可行,不在本文讨论范围之内。
“自己动手,丰衣足食”!在即将迎来招聘黄金季之际,将此文分享给读者,希望读者们也有一个良好的自动化测试开篇。
用Chrome浏览器测试,运行首页,首页无验证码字段。
import time
from selenium import webdriver
# 要测试的网站
weburl = 'XXX'
driver = webdriver.Chrome()
driver.maximize_window()
driver.get(weburl)
driver.implicitly_wait(5)
# 账号、密码
login = 'XXX'
password = 'XXX'
driver.find_element_by_id('loginName').send_keys(login)
driver.find_element_by_id('loginPassword').send_keys(password)
driver.find_element_by_id('submit').click()
time.sleep(5)
driver.quit()
(左右滑动查看完整代码)
上述代码实例中,用实际的测试数据代替“XXX”。
输入式验证码
这种是最简单的一种,只要识别出里面的内容,然后填入到输入框中即可,也是企业内部网站很常用的一种。只要不是电商网站,一般用这种就足够了。
这也是本文着重介绍的技术。这种识别技术叫OCR,这里我们推荐使用Python的第三方库——tesserocr。
对于没有什么背影影响的验证码,直接通过这个库来识别就可以。但是对于有嘈杂的背景的验证码这种,直接识别识别率会很低,遇到这种我们就得需要先处理一下图片,先对图片进行灰度化,然后再进行二值化,再去识别,这样识别率会大大提高。
滑动式验证码
模拟人去拖动验证码的行为,点击按钮,然后看到了缺口的位置,最后把拼图拖到缺口位置处完成验证。
第一步点击按钮,第二步拖到缺口位置。
点击式的图文验证和图标选择
图文验证:通过文字提醒用户点击图中相同字的位置进行验证。
图标选择:给出一组图片,按要求点击其中一张或者多张,借用万物识别的难度阻挡机器。
这两种原理相似,只不过是一个是给出文字,点击图片中的文字,一个是给出图片,点出内容相同的图片。这两种没有特别好的方法,只能借助第三方识别接口来识别出相同的内容,然后再使用Selenium模拟点击即可。
宫格验证码
但是我们发现不一样的验证码个数是有限的,这里采用模版匹配的方法。
我觉得就好像暴力枚举,把所有出现的验证码保存下来,然后挑出不一样的验证码,按照拖动顺序命名,我们从左到右上下到下,设为1,2,3,4。上图的滑动顺序为4,3,2,1所以我们命名4_3_2_1.png,这里得手动。
当验证码出现的时候,用我们保存的图片一一枚举,与出现这种比较像素,方法见上面。如果匹配上了,拖动顺序就为4,3,2,1,然后使用Selenium模拟即可。
识别的依赖关系
Tesseract-ocr可进行光学字符识别,它在Python上的package是pytesseract,要使用pytesseract,就需要安装tesseract-ocr。
安装python-tesseract,Python-tesseract是Google Tesseract-OCR的Python包装器。
Python-tesseract是用于Python的光学字符识别(OCR)工具。也就是说,它将识别并“读取”图像中嵌入的文本,是Google Tesseract-OCR Engine的包装,也可以用作tesseract的独立调用脚本,因为它可以读取Pillow和Leptonica图像库支持的所有图像类型,包括jpeg、png、gif、bmp、tiff等。
此外,如果用作脚本,Python-tesseract将打印识别的文本,而不是将其写入文件。
pip install pillow
pip install pytesseract
pip install tesseract (有时需要执行多次)
pip install opencv-python
编写ocr.py得到识别的图片
#! usr/bin/env python
#-*- coding:utf-8 -*-
# created on Mar26th
import os
import time
import selenium
from selenium import webdriver
import pytest
from PIL import Image
import pytesseract
import argparse
import cv2
# construct the argument parse and parse the arguments
ap=argparse.ArgumentParser()
ap.add_argument("-i","--picture",required=True,help="path to input picture to be OCR'd")
ap.add_argument("-p","--preprocess",type=str,default="thresh",help="type of preprocessing to be done")
args=vars(ap.parse_args())
# load the example picture and convert it to grayscale
image=cv2.imread(args["picture"])
gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# check to see if we should apply thresholding to preprocess the picture
if args["preprocess"]=="thresh":
gray=cv2.threshold(gray,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
# make a check to see if median blurring should be done to remove noise
elif args["preprocess"]=="blur":
gray=cv2.medianBlur(gray,3)
# write the grayscale picture to disk as a temporary file so we can apply OCR to it
filename="{}.png".format(os.getpid())
cv2.imwrite(filename,gray)
# load the picture as a PIL/Pillow images,apply OCR,and then delete the temporary file
def image_ocr(image_path, output_txt_file_name):
image_text = pytesseract.image_to_string(image_path)
os.remove(filename)
with open(output_txt_file_name, 'w+', encoding='utf-8') as f:
f.write(image_text)
print(image_text)
f.close()
# show the output images
cv2.imshow("Image",image)
cv2.imshow("Output",gray)
cv2.waitKey(0)
(左右滑动查看完整代码)
执行脚本,即在ocr.py当前路径下,执行命令:python ocr.py --image images/verify.png
编写ocr2.py得到识别的文本
先安装tesseract-ocr-setup-4.00.00dev.exe,记住安装路径,例如E:\Program Files (x86)\Tesseract-OCR,再编写代码:
import urllib3
import pytesseract
from PIL import Image
# 创建网络请求
http = urllib3.PoolManager()
# XXX代替测试网址
res = http.request('get','XXX')
# 将下载的验证码,存储到指定路径
f = open('E:/pic/verify.png','wb+')
f.write(res.data)
f.close()
pytesseract.pytesseract.tesseract_cmd = 'E:/Program Files (x86)/Tesseract-OCR/tesseract.exe'
tessdata_dir_config = '--tessdata-dir "E:/Program Files (x86)/Tesseract-OCR/tessdata"'
image = Image.open("E:/pic/verify.png")
code = pytesseract.image_to_string(image)
print(code)
username='XXX'
password='XXX'
driver.find_element_by_id('username').send_keys(username)
driver.find_element_by_id('password').send_keys(password)
driver.find_element_by_id('verify').send_keys(code)
driver.find_element_by_id('login').click()
(左右滑动查看完整代码)
可以看到输出code的文本值:
有背景的输入验证码
第一部分:图像识别(验证码)
Tesserocr是一个简单,对pillow友好的包装,围绕tesseract-ocr API进行光学字符识别(OCR)。在安装tesserocr之前要先安装tesseract,tesseract 下载地址:http://digi.bib.uni-mannheim.de/tesseract。
因为tesseract与tesserocr有对应关系,注意选择匹配的版本下载:tesserocr v2.4.0 (tesseract 4.0.0)。
下载whl:https://github.com/simonflueckiger/tesserocr-windows_build/releases
Windows下安装tesseract,双击运行安装文件,勾选 Additional language data(download)选项来安装 OCR 识别支持的语言包,其他都按默认选项安装。
下载语言包的过程比较曲折,改为在git上下载需要的语言包,放在tessdata文件夹中,勾选这个选项的话,OCR便可以识别多国语言。
下载语言包https://github.com/tesseract-ocr/tessdata,简体中文,放到\tessdata下。
Tesseract设置环境变量:将安装路径添加到path环境变量中,例如E:\Program Files (x86)\Tesseract-OCR,并且为语言包添加到环境变量中,在环境变量中新建一个系统变量,变量名称为TESSDATA_PREFIX,tessdata是放置语言包的文件夹,一般在你安装tesseract的目录下。
即tesseract的安装目录就是tessdata的父目录,把TESSDATA_PREFIX的值设置为E:\Program Files (x86)\Tesseract-OCR \tessdata;C:\Python\Python37\tessdata即可。
接下来,再安装 tesserocr:
(1)pip install tesserocr pillow 一般会安装失败。
(2)安装失败,就在下边的2个地址下载一个whl文件:
Tesserocr。
GitHub:https://github.com/simonflueckiger/tesserocr-windows_build/releases
tesserocr PyPI:https://pypi.python.org/pypi/tesserocr
下载的是tesserocr-2.4.0-cp37-cp37m-win_amd64.whl。
(3)安装anaconda:https://www.anaconda.com/distribution/,下载安装包,双击运行安装文件,选择目标安装路径E:\dev\anaconda3,其他按默认安装。
添加到环境变量-系统变量path中,TESSDATA_PREFIX的值=C:\Program Files (x86)\Tesseract-OCR\tessdata。
将tesserocr-2.4.0-cp37-cp37m-win_amd64.whl拷贝到用户的家目录下,比如C:\Users\用户,再用pip安装这个whl文件。
pip install tesserocr-2.4.0-cp37-cp37m-win_amd64.whl
(左右滑动查看完整代码)
或者把tesserocr-2.4.0-cp37-cp37m-win_amd64.whl文件拷贝到C:\Python37\Lib\site-packages下,再执行pip install tesserocr-2.4.0-cp37-cp37m-win_amd64.whl命令,否则会提示找不到该文件路径。
安装完成tesserocr-2.4.0:
TESSDATA_PREFIX的值注意这里不要加“;”,否则可能会遇到RuntimeError: Failed to init API, possibly an invalid tessdata path: E:\Program Files (x86)\Tesseract-OCR \tessdata/的报错。
运行py文件:
from PIL import Image
import tesserocr
image = Image.open('14252.png')
result = tesserocr.image_to_text(image)
print(result)
(左右滑动查看完整代码)
结果为0278:
注意:
截图的文件类型应为png,若是jpg,截图函数可以跑通,但是后面切割识别会出错!
第二部分:图像切割(验证码)
def jietu():
elementcode=driver.find_element_by_id('verify_img')
# 获取左上角坐标
left=elementcode.location['x']
top=elementcode.location['y']
# 获取右下角坐标
right=elementcode.size['width']+left
bottom=elementcode.size['height']+top
# print(left,top,right,bottom)
# 切割
img=Image.open(screen_save_path)
codeimg=img.crop((left,top,right,bottom))
codeimg.save("E:/pic/verifycode.png")
# 第三方接口,后面会具体介绍
def coderegconize():
try:
r = ShowapiRequest("https://route.showapi.com/932-2", "168625", "566bb29c35da4556953c03ed41dfedd8")
# 接口的参数
r.addBodyPara("typeId", "14")
r.addBodyPara("convert_to_jpg", "0")
r.addFilePara("image", "E:/pic/verifycode.png")
picture = Image.open("E:/pic/verifycode.png")
# code = pytesseract.image_to_string(picture)
regconizecode = tesserocr.image_to_text(picture)
# print(type(regconizecode))
print("识别结果为:",regconizecode)
return regconizecode
except Exception as e:
print("未识别到验证码")
jietu()
coderegconize()
(左右滑动查看完整代码)
第三方接口
对于有背景的验证码,推荐第三方接口——易源,网址https://www.showapi.com/console#/dashboard,具体参数说明请参考易源网站,这里就不引用了。
rq = ShowapiRequest("https://route.showapi.com/184-4", "193284","991cd8e44cbb45bc92e2a83353888fdd")
# r.addBodyPara("img_base64", "")
rq.addBodyPara("typeId", "35")
rq.addBodyPara("convert_to_jpg", "1")
rq.addBodyPara("needMorePrecise", "1")
rq.addFilePara("image", "./images/verifyimg.png")
result = rq.post().json()
# print(result)
recognizecode = result['showapi_res_body']['Result']
print("识别结果为:", recognizecode)
(左右滑动查看完整代码)
附上完整的代码:
def setUp(self):
# XXX代替要测试的网站
weburl = 'XXX'
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.get(weburl)
time.sleep(5)
print("web login...")
number = 3
for i in range(number):
try:
file = os.path.abspath(os.path.join(os.path.dirname(__file__), ".")) + '/picture/'
now = 'nowpic'
screenfile = file + now + '.png'
self.driver.save_screenshot(screenfile)
def codeimage():
codelement = self.driver.find_element_by_id('verify_img')
left = codelement.location['x']
top = codelement.location['y']
right = codelement.size['width'] + left
bottom = codelement.size['height'] + top
# print(left,top,right,bottom)
img = Image.open(screenfile)
codeimg = img.crop((left, top, right, bottom))
codeimg.save("./images/verifyimg.png")
# 验证码图片函数调用
codeimage()
def coderecognize():
try:
rq = ShowapiRequest("https://route.showapi.com/184-4", "193284","991cd8e44cbb45bc92e2a83353888fdd")
# r.addBodyPara("img_base64", "")
rq.addBodyPara("typeId", "35")
rq.addBodyPara("convert_to_jpg", "1")
rq.addBodyPara("needMorePrecise", "1")
rq.addFilePara("image", "./images/verifyimg.png")
result = rq.post().json()
# print(result)
recognizecode = result['showapi_res_body']['Result']
print("识别结果为:", recognizecode)
return recognizecode
except Exception as e:
print("未识别到验证码")
# XXX代替账号、密码
user = 'XXX'
pwd = 'XXX'
codevalue = coderecognize()
self.driver.find_element_by_name('username').clear()
self.driver.find_element_by_name('username').send_keys(user)
self.driver.find_element_by_name('password').clear()
self.driver.find_element_by_name('password').send_keys(pwd)
self.driver.find_element_by_name('verify').clear()
self.driver.find_element_by_name('verify').send_keys(codevalue)
self.driver.find_element_by_class_name('beg-pull-right').click()
self.driver.implicitly_wait(20)
assert ‘首页' in self.driver.find_element_by_id('left_side_10001').text
print("登录的用例执行通过")
break
except Exception as e:
print("login failed.")
time.sleep(2)
def tearDown(self):
driver = self.driver
element = driver.find_elements_by_class_name('layui-nav-item')[2]
ActionChains(driver).move_to_element(element).perform()
time.sleep(2)
driver.find_element_by_class_name('confirm-rst-url-btn-logout').click()
time.sleep(2)
driver.find_element_by_class_name('layui-layer-btn0').click()
time.sleep(1)
assert driver.find_element_by_name('username')
print("web logout.")
driver.quit()
(左右滑动查看完整代码)
官网:https://pypi.org/project/pytesseract/以及各大网站博客,致谢所有朋友!