做爬蟲已經有半年時間了,感覺已經踏入了這個行業的門檻了,要想做到更好還要花大量的時間去提升自己。
在做爬蟲的時候相信大家肯定會遇到驗證碼的問題,那麼爬蟲的時候遇到了驗證碼該怎麼辦?目前最簡單的辦法就是打碼平台,打碼平台這裡就不多說了,但是打碼平台也有缺點呢,比如,收費,并不是實時,可能會有幾秒或者幾十秒的等待時間等,這些都會影響到爬蟲的效率問題。那麼有沒有不收費,而且速度又快呢?答案是肯定的,小編今天過來就是告訴你該怎麼去解決這個事情的。
最開始的時候我是用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 = ; x < w; x++)
{
for (int y = ; y < h; y++)
{
int argb = bufferedImage.getRGB(x, y);
int r = (int) (((argb >> ) & ) * + );
int g = (int) (((argb >> ) & ) * + );
int b = (int) (((argb >> ) & ) * + );
if (r >= )
{
r = ;
}
if (g >= )
{
g = ;
}
if (b >= )
{
b = ;
}
gray[x][y] = (int) Math
.pow((Math.pow(r, ) * + Math.pow(g, )
* + Math.pow(b, ) * ), / );
}
}
int threshold = ostu(gray, w, h);
BufferedImage binaryBufferedImage = new BufferedImage(w, h,
BufferedImage.TYPE_BYTE_BINARY);
for (int x = ; x < w; x++)
{
for (int y = ; y < h; y++)
{
if (gray[x][y] > threshold)
{
gray[x][y] |= ;
} else
{
gray[x][y] &= ;
}
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() <= )
{
return true;
}
return false;
}
public static boolean isWhite(int colorInt)
{
Color color = new Color(colorInt);
if (color.getRed() + color.getGreen() + color.getBlue() > )
{
return true;
}
return false;
}
public static int isBlackOrWhite(int colorInt)
{
if (getColorBright(colorInt) < || getColorBright(colorInt) > )
{
return ;
}
return ;
}
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 = ; x < w; x++)
{
for (int y = ; y < h; y++)
{
int red = & gray[x][y];
histData[red]++;
}
}
// Total number of pixels
int total = w * h;
float sum = ;
for (int t = ; t < ; t++)
sum += t * histData[t];
float sumB = ;
int wB = ;
int wF = ;
float varMax = ;
int threshold = ;
for (int t = ; t < ; t++)
{
wB += histData[t]; // Weight Background
if (wB == )
continue;
wF = total - wB; // Weight Foreground
if (wF == )
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(, , , ));
subImgs.add(img.getSubimage(, , , ));
subImgs.add(img.getSubimage(, , , ));
subImgs.add(img.getSubimage(, , , ));
return subImgs;
}
,
,
,
這個是資源庫的圖檔注意命名格式:
4,第四步是最為關鍵的一步,把整個流程寫成一套程式之後,程式執行到第四步了,就要拿本次下載下傳的驗證碼,與資源庫中的切割後的驗證碼進行比較,一旦比對成功,那麼久傳回資源庫中相應圖檔對應的檔案名,到這裡圖檔内容就解析出來了,最後把傳回的結果拼成串就是這個驗證碼的結果。
為了大家更好的去研究關于更好的解決驗證碼的解析問題,我下去還會研究更好的解決方法,去解析更加複雜的驗證碼。這個程式能夠解析最複雜的驗證碼應該就是
這樣的了。由于這個驗證碼字元之間的間隔大小不确定導緻我截取資源的圖檔是截了大量的圖檔,最後成功率大概在百分之八十左右,可以算是成功了。
程式下載下傳位址http://download.csdn.net/download/javamanjosen/9731540
利用svm解析驗證碼技術,在我做爬蟲的這段時間幫助我解決了不少的事情,到目前為止,遇到的複雜驗證碼還是采用打碼平台。目前精力有限,以後還會在驗證碼上尋找更好的方法來解析更複雜的驗證碼,有哪位對解析驗證碼或者對爬蟲比較感興趣的可以交流小編郵箱:[email protected]。