在做自动化测试或压力测试时,验证码总是一个问题。在以往的压力测试经历中,测试一般在独立的测试环境中进行,可以放心禁用验证码或使用万能验证码,这个是最实用的。但是,这两天我尝试了一个使用第三方的图形图像识别工具来完成验证码识别并通过Jmeter完成登录的过程,识别工具的识别成功率有限,因此本篇估计仅能在理论范围内适用。

本篇内容大部分内容来自于该作者的文章:http://blog.csdn.net/xreztento/article/details/48682923

总体目的:给Jmeter写一个后置处理器,用来将上一个请求响应返回的验证码图片识别成文字,并将识别内容保存为Jmeter的一个参数,这个参数供登录post请求进行登录验证,从而完成登录的自动化过程。

工具

(1)第三方图形图像识别工具:tesseract-ocr  下载地址:http://code.google.com/p/tesseract-ocr/downloads/list  基本无法下载,已上传到我的百度网盘

安装后,可以在cmd下试一试是否安装成功:

在cmd下输入命令:tesseract d:\123.jpg result -l eng   意思是将D盘下的123.jpg 识别后放在result.txt下

(2)需要用到的jar包:

Jmeter插件开发相关的jar包: ApacheJmeter_core.jar jorphon.jar logkit-2.0.jar  这些在Jmeter的lib中都有 直接导入工程项目即可

图形处理相关的jar包:jai-imageio-1.1.jar  swingx-1.6.1.jar  从网上下的,已上传到百度云盘 jar 文件夹下

插件开发

用java IDE新建一个工程项目,实现两个部分,一个是识别图片,一个是Jmeter插件的UI部分。工程项目完成目录为:

package com.test.huu;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Locale; import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream; import com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam; public class ImageIOHelper{
public static File createImage(File imageFile, String imageFormat) { File tempFile = null;
ImageInputStream iis = null;
ImageOutputStream ios = null;
ImageReader reader = null;
ImageWriter writer = null; try {
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(imageFormat);
reader = readers.next(); iis = ImageIO.createImageInputStream(imageFile);
reader.setInput(iis); IIOMetadata streamMetadata = reader.getStreamMetadata();
TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.CHINESE);
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("tiff");
writer = writers.next(); BufferedImage bi = removeBackgroud(reader.read(0));
IIOImage image = new IIOImage(bi,null,reader.getImageMetadata(0));
tempFile = tempImageFile(imageFile); ios = ImageIO.createImageOutputStream(tempFile);
writer.setOutput(ios);
writer.write(streamMetadata, image, tiffWriteParam); } catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(iis != null){
try {
iis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ios != null){
try {
ios.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(writer != null){
writer.dispose();
}
if(reader != null){
reader.dispose();
} }
return tempFile;
} private static File tempImageFile(File imageFile) {
String path = imageFile.getPath();
StringBuffer strB = new StringBuffer(path);
return new File(strB.toString().replaceFirst("jpg", "tif"));
} //给图片降噪 提高识别度
public static int isFilter(int colorInt) {
Color color = new Color(colorInt);
if ((color.getRed() > 85 && color.getRed() < 255)
&& (color.getGreen() > 85 && color.getGreen() < 255)
&& (color.getBlue() > 85 && color.getBlue() < 255)) {
return 1;
}
return 0;
} public static BufferedImage removeBackgroud(BufferedImage img)
throws Exception {
int width = img.getWidth();
int height = img.getHeight();
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
if (isFilter(img.getRGB(x, y)) == 1) {
img.setRGB(x, y, Color.WHITE.getRGB());
}
}
}
return img;
} }

ImageIOHelper.java

package com.test.huu;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List; public class OCR {
private final String LANG_OPTION = "-l";
private final String EOL = System.getProperty("line.separator");
private String tessPath = "D://Program Files (x86)//Tesseract-OCR"; public String recognizeText(File imageFile,String imageFormat) {
File tempImage = ImageIOHelper.createImage(imageFile,imageFormat);
File outputFile = new File(imageFile.getParentFile(),"output" + imageFile.getName());
StringBuffer sb = new StringBuffer();
List<String> cmd = new ArrayList<String>(); cmd.add(tessPath+"//tesseract");
cmd.add("");
cmd.add(outputFile.getName());
cmd.add(LANG_OPTION);
cmd.add("eng");
ProcessBuilder pb = new ProcessBuilder();
pb.directory(imageFile.getParentFile()); cmd.set(1, tempImage.getName());
pb.command(cmd);
pb.redirectErrorStream(true); Process process = null;
BufferedReader in = null;
int wait;
try {
process = pb.start();
//tesseract.exe xxx.tif 1 -l eng
wait = process.waitFor();
if(wait == 0){
in = new BufferedReader(new InputStreamReader(new FileInputStream(outputFile.getAbsolutePath()+".txt"),"UTF-8"));
String str;
while((str = in.readLine())!=null){
sb.append(str).append(EOL);
}
in.close(); }else{ tempImage.delete();
}
new File(outputFile.getAbsolutePath()+".txt").delete();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} tempImage.delete();
return sb.toString();
}
}

OCR.java

package com.test.huu;

import java.io.File;  

public class TestOCR {  

 public static void main(String[] args) {
String path = "D://124.jpg";
System.out.println("ORC Test Begin......");
try {
String valCode = new OCR().recognizeText(new File(path), "jpeg");
System.out.println(valCode);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("ORC Test End......");
} }

TestOCR.java

package com.test.huu;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable; import org.apache.jmeter.processor.PostProcessor;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.AbstractScopedTestElement;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger; public class VcodeExtractor extends AbstractScopedTestElement implements PostProcessor, Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
/**
*
*/
private static final Logger log = LoggingManager.getLoggerForClass(); @Override
public void process() { // TODO Auto-generated method stub
JMeterContext context = getThreadContext();
SampleResult previousResult = context.getPreviousResult();
if (previousResult == null) {
return;
}
log.debug("VcodeExtractor processing result"); String status = previousResult.getResponseCode();
int id = context.getThreadNum();
// String imageName = id + ".jpg";
String path = "D://" + id + ".jpg"; if(status.equals("200")){
byte[] buffer = previousResult.getResponseData();
FileOutputStream out = null;
File file = null;
try {
file = new File(path);
out = new FileOutputStream(file);
out.write(buffer);
out.flush(); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(out != null){
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} try {
String vcode = new OCR().recognizeText(file, "jpeg");
vcode = vcode.replace(" ", "").trim(); JMeterVariables var = context.getVariables();
var.put("vcode", vcode);
// var.put("vuser", String.valueOf(id));
} catch (Exception e) {
e.printStackTrace();
}
} } }

VodeExtractor.java

package com.test.huu;
import org.apache.jmeter.processor.gui.AbstractPostProcessorGui;
import org.apache.jmeter.testelement.TestElement; public class VcodeExtractorGUI extends AbstractPostProcessorGui{ /**
*
*/
private static final long serialVersionUID = 1L; /**
*
*/
@Override
public TestElement createTestElement() {
// TODO Auto-generated method stub
VcodeExtractor extractor = new VcodeExtractor();
modifyTestElement(extractor);
return extractor;
} @Override
public String getLabelResource() {
// TODO Auto-generated method stub
return this.getClass().getName();
} @Override
public String getStaticLabel() {//设置显示名称
// TODO Auto-generated method stub
return "VcodeExtractor";
} @Override
public void modifyTestElement(TestElement extractor) {
// TODO Auto-generated method stub
super.configureTestElement(extractor); }
}

VcodeExtractorGUI.java

插件生成

插件开发完成后,在Eclipse中 export-Runnable jar file ,将必要的依赖库加进去,最后会生成一个 .jar 文件

注意:图形相关的jar包  直接使用时会报错(Jmeter会报一个错:java.lang.IllegalArgumentException: vendorName == null) 最终在网上找到了解决方案

生成jar包后,用解压工具打开,将 /META-INF 目录下的 MANIFEST.MF 文件用编辑器(我用的是sublime)打开,拷贝进去下面一段代码,保存压缩包:

Specification-Title: Java Advanced Imaging Image I/O Tools
Specification-Version: 1.1
Specification-Vendor: Sun Microsystems, Inc.
Implementation-Title: com.sun.media.imageio
Implementation-Version: 1.1
Implementation-Vendor: Sun Microsystems, Inc.

插件插入Jmeter

将 .jar 文件放入Jmeter 安装路径下 lib/ext/ 目录下,重启Jmeter

可以看到,我们新开发的后置处理器 VcodeExtractor

再看下大致的登录过程测试计划:

登录的post请求参数中,可以使用Vcode,Vcode是我们开发的后置处理器 VcodeExtrator 返回的从图片验证码中识别出来的字符串

插件效果验证

把测试计划跑一次  根据察看结果树  看下效果

登录请求成功啦,但是图片识别也不是百分百成功,部分失败情况下,登录请求肯定会失败。Tesseract-OCR也有训练识别的功能,但是不再继续研究了。

关于Tesseract-OCR的延展性学习可参考:

(1)http://www.cnblogs.com/alex-blog/archive/2012/10/08/2714984.html

(2)http://blog.csdn.net/ycb1689/article/details/8520954

(3)http://www.52itstyle.com/thread-4803-1-1.html

Jmeter—实现识别验证码登录的更多相关文章

  1. selenium自动化 | 借助百度AI开放平台识别验证码登录职教云

    #通过借助百度AI开放平台识别验证码登录职教云 from PIL import Image from aip import AipOcr import unittest # driver.get(zj ...

  2. 百度api识别验证码登录

    import time from selenium import webdriver from aip import AipOcr def initial(): """ ...

  3. jmeter跳过验证码登录配置:通过手动添加 Cookie 跳过带验证码的登录接口

    目录 一.基本配置 二.HTTP请求默认值 三.HTTP信息头管理器 四.HTTP Cookie管理器 五.线程组下接口设置 一.基本配置 二.HTTP请求默认值 (1)jmeter的设置: (2)设 ...

  4. 微博验证码的识别并登录获取cookies

    记得以前微博是用的宫格验证码,现在不一样了,用的是滑块验证码和 点触验证码,每天登陆的第一次基本用的是滑块,继续登录就都用的是点触验证码.所以滑块验证码不写,感兴趣的可以补上. 代码: 这里用的超级鹰 ...

  5. 【转载】loadrunner使用system()函数调用Tesseract-OCR识别验证码遇到的问题

    俗话说前人栽树,后人乘凉,此话一点不假,结合云层的一遍文章:http://bbs.51testing.com/thread-533920-1-1.html,知道还有一个Tesseract-OCR可以用 ...

  6. loadrunner使用system()函数调用Tesseract-OCR识别验证码遇到的问题

    俗话说前人栽树,后人乘凉,此话一点不假,结合云层的一遍文章:http://bbs.51testing.com/thread-533920-1-1.html,知道还有一个Tesseract-OCR可以用 ...

  7. 使用python识别验证码

    公司的登录注册等操作有验证码,测试环境可以让开发屏蔽掉验证码,但是如果到线上的话就要想办法识别验证码或必过验证码了. 识别验证码主要分为三部分,一.对验证码进行二值化.二.将二值化后的图片分割.三.进 ...

  8. python网络爬虫之如何识别验证码

    有些网站的登录方式是验证码登录的方式,比如今天我们要测试的网站专利检索及分析. http://www.pss-system.gov.cn/sipopublicsearch/portal/uilogin ...

  9. Python 实现简单图片验证码登录

    朋友说公司要在测试环境做接口测试,登录时需要传入正确的图片的验证码,本着懒省事的原则,推荐他把测试环境的图片验证码写死,我们公司也是这么做的^_^.劝说无果/(ㄒoㄒ)/~~,只能通过 OCR 技术来 ...

随机推荐

  1. 【 js 性能优化】【源码学习】underscore throttle 与 debounce 节流

    在看 underscore.js 源码的时候,接触到了这样两个方法,很有意思: 我先把实现的代码撂在下面,看不懂的可以先跳过,但是跳过可不是永远跳过哦- 一个是 throttle: _.throttl ...

  2. 编译java代码出现 错误: 需要class, interface或enum 提示

          出现这种错误: 需要class, interface或enum 提示,一般分两种情况: 1.代码编写有误: 2.编码器选择编码格式有问题.(主要是用非记事本编写代码文件,存在编码格式转换问 ...

  3. 使用LINQ查询数据实例和理解

    使用LINQ查询数据实例和理解 var contacts= from customer in db.Customers where customer.Name.StartsWith("A&q ...

  4. python实现贪婪算法解决01背包问题

    一.背包问题 01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn.01背包是背包问题中最简单的问题.01背包的约束条件是给定几种物 ...

  5. Atcoder:AGC004F Namori

    传送门 先考虑树,树是一个二分图. 看到是二分图并且每次是对两边的同色的点反色可以想到转化:让奇数层的点为黑,偶数为白,变成每次可以交换两个点的颜色. 把黑看成 \(-1\),白看成 \(1\),那么 ...

  6. Leaflet.draw 无法编辑multipolygon类型多边形 解决方法

    问题说明 在做面要素的编辑的时候,当对multiploygon类型的面要素进行编辑的时候,出现如下错误: TypeError: Cannot read property 'lat' of null 通 ...

  7. JSTL核心标签库——<c:set>标签、<c:out>标签

    <c:set>标签 index.jsp <%@ page import="java.util.Map" %> <%@ page import=&quo ...

  8. [WPF 基础知识系列] —— 绑定中的数据校验Vaildation

    前言: 只要是有表单存在,那么就有可能有对数据的校验需求.如:判断是否为整数.判断电子邮件格式等等. WPF采用一种全新的方式 - Binding,来实现前台显示与后台数据进行交互,当然数据校验方式也 ...

  9. org.postgresql.util.PSQLException: 栏位索引超过许可范围:3,栏位数:2。

    org.postgresql.util.PSQLException: 栏位索引超过许可范围:3,栏位数:2. 今天在写完SQL进行查询的时候,后台一直报错显示上面的信息.看错误完全不知道原因,就重新检 ...

  10. MySQL主从复制——主库已有数据的解决方案

    在上篇文章中我们介绍了基于Docker的Mysql主从搭建,一主多从的搭建过程就是重复了一主一从的从库配置过程,需要注意的是,要保证主从库my.cnf中server-id的唯一性.搭建完成后,可以在主 ...