良好的开端是自动化测试成功的一半:攻克首页验证码登录难题

科技   2024-07-10 17:30   上海  

出品|51Testing软件测试圈


俗语说得好:良好的开端是成功的一半,在Web自动化测试中,首页登录是每个测试用例执行的前提。


首页登录有验证码,也有为了安全而设置的验证码,对这些验证码进行自动识别,提取文字进行登录,是自动化测试良好的开端,否则一切都无从谈起。


至于有些博客提到开发设置万能码等,如果开发团队支持也是可行,不在本文讨论范围之内。


“自己动手,丰衣足食”!在即将迎来招聘黄金季之际,将此文分享给读者,希望读者们也有一个良好的自动化测试开篇。




首页登录——无验证码


用Chrome浏览器测试,运行首页,首页无验证码字段。

import timefrom 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 osimport timeimport seleniumfrom selenium import webdriverimport pytestfrom PIL import Imageimport pytesseractimport argparseimport cv2

# construct the argument parse and parse the argumentsap=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 grayscaleimage=cv2.imread(args["picture"])gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# check to see if we should apply thresholding to preprocess the pictureif 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 noiseelif args["preprocess"]=="blur": gray=cv2.medianBlur(gray,3)
# write the grayscale picture to disk as a temporary file so we can apply OCR to itfilename="{}.png".format(os.getpid())cv2.imwrite(filename,gray)
# load the picture as a PIL/Pillow images,apply OCR,and then delete the temporary filedef 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 imagescv2.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 urllib3import pytesseractfrom 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 Imageimport 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/以及各大网站博客,致谢所有朋友!

END

声明:本文为51Testing软件测试网枫叶用户投稿内容,该用户投稿时已经承诺独立承担涉及知识产权的相关法律责任,并且已经向51Testing承诺此文并无抄袭内容。发布本文的用途仅仅为学习交流,不做任何商用,未经授权请勿转载,否则作者和51Testing有权追究责任。如果您发现本公众号中有涉嫌抄袭的内容,欢迎发送邮件至:editor@51testing.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。


点分享
点收藏
点在看
点点赞
51Testing软件测试圈
博为峰20周年,青春正当燃,一起向未来! 博为峰51Testing软件测试圈——坚持以专业技术为核心,关注软件测试领域最前沿技术和管理思想,凝聚行业力量,共同分享软件测试理论与实践经验,是一个测试人的生活与技术圈。
 最新文章