做爬虫已经有半年时间了,感觉已经踏入了这个行业的门槛了,要想做到更好还要花大量的时间去提升自己。
在做爬虫的时候相信大家肯定会遇到验证码的问题,那么爬虫的时候遇到了验证码该怎么办?目前最简单的办法就是打码平台,打码平台这里就不多说了,但是打码平台也有缺点呢,比如,收费,并不是实时,可能会有几秒或者几十秒的等待时间等,这些都会影响到爬虫的效率问题。那么有没有不收费,而且速度又快呢?答案是肯定的,小编今天过来就是告诉你该怎么去解决这个事情的。
最开始的时候我是用JavaOCR解析验证码的,但是发现验证码往往都是比较复杂,对于复杂的验证码来讲用Javaocr进行解析正确率会大大降低,有一天从浏览网页的时候发现可以利用SVM进行图像分类(识别),利用svm的这个功能,展开了对验证码解析的研究。
用SVM解析验证码步骤:
1,下载验证码;
2,切割验证码并整理成资源库
3,验证码与资源库中切割后的验证码进行比较;
4,返回验证码的结果;
我按照这样的方法去写的时候发现正确率没有自己想象中的那么高
改进后的步骤:
1,下载图片;
2,去除干扰线背景;
3,切割验证码,并保存到资源库中;
4,验证码与资源库进行对比;
5,返回结果;
上下对比发现仅仅多了第二步,在这里我特别强调一下,一个好的去背景的算法能大大提高图片识别正确率;
下面说说,具体怎么做:
1,验证码下载地址
这个是下载的验证码:
2,去图片背景:
先付一张去背景候的图片
去背景的效果还是很可观的;
程序代码:
public static void cleanImage(File sfile, String destDir)
throws IOException
{
File destF = new File(destDir);
if (!destF.exists())
{
destF.mkdirs();
}
BufferedImage bufferedImage = ImageIO.read(sfile);
int h = bufferedImage.getHeight();
int w = bufferedImage.getWidth();
int[][] gray = new int[w][h];
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
int argb = bufferedImage.getRGB(x, y);
int r = (int) (((argb >> 16) & 0xFF) * 1.1 + 30);
int g = (int) (((argb >> 8) & 0xFF) * 1.1 + 30);
int b = (int) (((argb >> 0) & 0xFF) * 1.1 + 30);
if (r >= 255)
{
r = 255;
}
if (g >= 255)
{
g = 255;
}
if (b >= 255)
{
b = 255;
}
gray[x][y] = (int) Math
.pow((Math.pow(r, 2.2) * 0.2973 + Math.pow(g, 2.2)
* 0.6274 + Math.pow(b, 2.2) * 0.0753), 1 / 2.2);
}
}
int threshold = ostu(gray, w, h);
BufferedImage binaryBufferedImage = new BufferedImage(w, h,
BufferedImage.TYPE_BYTE_BINARY);
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
if (gray[x][y] > threshold)
{
gray[x][y] |= 0x00FFFF;
} else
{
gray[x][y] &= 0xFF0000;
}
binaryBufferedImage.setRGB(x, y, gray[x][y]);
}
}
/*for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (isBlack(binaryBufferedImage.getRGB(x, y)))
{
System.out.print("*");
} else
{
System.out.print(" ");
}
}
System.out.println();
} */
ImageIO.write(binaryBufferedImage, "jpg", new File(destDir, sfile
.getName()));
}
public static boolean isBlack(int colorInt)
{
Color color = new Color(colorInt);
if (color.getRed() + color.getGreen() + color.getBlue() <= 300)
{
return true;
}
return false;
}
public static boolean isWhite(int colorInt)
{
Color color = new Color(colorInt);
if (color.getRed() + color.getGreen() + color.getBlue() > 300)
{
return true;
}
return false;
}
public static int isBlackOrWhite(int colorInt)
{
if (getColorBright(colorInt) < 30 || getColorBright(colorInt) > 730)
{
return 1;
}
return 0;
}
public static int getColorBright(int colorInt)
{
Color color = new Color(colorInt);
return color.getRed() + color.getGreen() + color.getBlue();
}
public static int ostu(int[][] gray, int w, int h)
{
int[] histData = new int[w * h];
// Calculate histogram
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
int red = 0xFF & gray[x][y];
histData[red]++;
}
}
// Total number of pixels
int total = w * h;
float sum = 0;
for (int t = 0; t < 256; t++)
sum += t * histData[t];
float sumB = 0;
int wB = 0;
int wF = 0;
float varMax = 0;
int threshold = 0;
for (int t = 0; t < 256; t++)
{
wB += histData[t]; // Weight Background
if (wB == 0)
continue;
wF = total - wB; // Weight Foreground
if (wF == 0)
break;
sumB += (float) (t * histData[t]);
float mB = sumB / wB; // Mean Background
float mF = (sum - sumB) / wF; // Mean Foreground
// Calculate Between Class Variance
float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);
// Check if new maximum found
if (varBetween > varMax)
{
varMax = varBetween;
threshold = t;
}
}
return threshold;
}
这个区背景算法还是可以的,很多地方都会用的到,建议收藏起来用。
3,把图片切成块
比如例子中的验证码有4个字符,那么就分成4块,例子中的验证码只包含阿拉伯数字,那么资源库里面的验证码切块(图片)至少要包含0-9十个数字字符,因为第四步中要进行对比。特别注意切割验证码的时候一定要找到合适的位置,千万不能切偏。
import java.awt.image.BufferedImage;//用到了这个包
public static List<BufferedImage> splitImage(BufferedImage img)
throws Exception {
List<BufferedImage> subImgs = new ArrayList<BufferedImage>();
subImgs.add(img.getSubimage(6, 2, 11, 15));
subImgs.add(img.getSubimage(19, 2, 11, 15));
subImgs.add(img.getSubimage(32, 2, 11, 15));
subImgs.add(img.getSubimage(45, 2, 11, 15));
return subImgs;
}
,,,
这个是资源库的图片注意命名格式:
4,第四步是最为关键的一步,把整个流程写成一套程序之后,程序执行到第四步了,就要拿本次下载的验证码,与资源库中的切割后的验证码进行比较,一旦匹配成功,那么久返回资源库中相应图片对应的文件名,到这里图片内容就解析出来了,最后把返回的结果拼成串就是这个验证码的结果。
为了大家更好的去研究关于更好的解决验证码的解析问题,我下去还会研究更好的解决方法,去解析更加复杂的验证码。这个程序能够解析最复杂的验证码应该就是这样的了。由于这个验证码字符之间的间隔大小不确定导致我截取资源的图片是截了大量的图片,最后成功率大概在百分之八十左右,可以算是成功了。
程序下载地址http://download.csdn.net/download/javamanjosen/9731540
利用svm解析验证码技术,在我做爬虫的这段时间帮助我解决了不少的事情,到目前为止,遇到的复杂验证码还是采用打码平台。目前精力有限,以后还会在验证码上寻找更好的方法来解析更复杂的验证码,有哪位对解析验证码或者对爬虫比较感兴趣的可以交流小编邮箱:shangzhen2014@outlook.com。