引言

近来在写项目时,需要抓取学校的学生信息,可是学校的登录验证码对于模拟登录实在很难搞,随后经过尝试利用代码成功解析了智慧教室的登录验证码,特此将解析过程作以简单记录。

学校验证码

首先对学校验证码作以简单分析,如上图。经过初步分析,可以发现智慧教室的登录验证码是由5个不同取值的数字构成(**先暂时忽略掉颜色的干扰因素**)。而这5个数字分布于图片的5个位置上,每个位置的数字取值范围都为0,1,2,3,5,6,7,8,而获取到足够多的样本后发现同一取值的数字在不同位置的形态是不同的。由此可以得出结论,除去颜色因素的干扰,这个图片的所有数字共有5个位置*8个取值,即40种不同的数字结果。

解析思路

很显然,图片除过白色背景剩下的几乎都是数字的颜色以及一些干扰颜色点。首先将白色以外的部分全部渲染成黑色,效果如下:

渲染主要代码如下:

        int width = img.getWidth();
		int height = img.getHeight();
		int start = 0;
		int end = 0;
		Label1: for (int y = 0; y < height; ++y) {
			for (int x = 0; x < width; ++x) {
				if (isBlack(img.getRGB(x, y)) == 1) {
					start = y;
					break Label1;
				}
			}
		}
		Label2: for (int y = height - 1; y >= 0; --y) {
			for (int x = 0; x < width; ++x) {
				if (isBlack(img.getRGB(x, y)) == 1) {
					end = y;
					break Label2;
				}
			}
		}
		return img.getSubimage(0, start, width, end - start + 1);

如此一来需要应对的颜色就只有黑白。在分析验证码图片时提到过,所有数字共有40种数字结果,现在通过从智慧教室不断获取验证码图,将每一张验证码图分割为5份,代表其5个位置,得到这40种不同结果并且将它们也处理成黑白的效果。如下图:

文件名第一个数字代表该数字的取值,第二个数字代表其在验证码图片中的位置。
ps:获得的这40种数字结果可以理解为为解析比对验证码的基础构成数据

ok,现在需要获取的基本都拿到了,接下来开始真正的解析。
在得到黑白两色的验证码图片后,也将该验证码分为5个部分即五个位置进行分析,,将每一个位置的数字形态与获得的40种数字进行比对,由于经过处理仅有黑白两种颜色,且除白色背景外,黑色大部分都是数字的颜色,而且其上的干扰像素点由于相对数量较少所以可以忽略。故将这40种数字与验证码图片相应位置的数字作对比,而与验证码相应位置的数字不同像素点最少的数字即为该位置的值(通过40种数字的文件名判断它本身的值)。以下为分析主要代码:
(ps:之所以取最少,是因为由于干扰颜色点的存在使得几乎很难出现对比结果完全一样的数字,而且由于取的是最少值,有些极端情况会导致结果不准确,例如:5与6如果干扰颜色点过多就及其有可能5的对比结果略小于6而导致错误。):

    String result = "null";
	int width = img.getWidth();
	int height = img.getHeight();
	int min = width * height;
	for (BufferedImage baseData : map.keySet()) {
		int count = 0;
		if (Math.abs(baseData.getWidth()-width) > 2)
			continue;
		int widthmin = width < baseData.getWidth() ? width :baseData.getWidth();
		int heightmin = height < baseData.getHeight() ? height : baseData.getHeight();
	Label1: for (int x = 0; x < widthmin; ++x) {
			    for (int y = 0; y < heightmin; ++y) {
					if (isBlack(img.getRGB(x, y)) != isBlack(baseData.getRGB(x, y))) {
						count++;
						if (count >= min)
							break Label1;
					}
				}
			}
			if (count < min) {
				min = count;
				result = map.get(bi);
			}
		}
		return result;

总结

分析过程大致如此,完毕^_^

————没有什么是停不下来的,除了时间