這幾年Python爬蟲特別的火,我有個朋友是一個Python爬蟲工程師,本人菜雞Java開發工程師一名,最近所做的一個項目是需要去爬一個網頁的數據,但是進入網頁需要登錄,登錄需要輸入圖片驗證碼。爬蟲的第三方jar包用的是jsoup,圖片識別用的是tesj4j。話不多碩,上demo,奧利給!
一、下載jsoup.jar、tess4j的jar包,或者maven引入jsoup和tess4j的jar包。在windows環境下,但是tess4j本地開發需要調用dll文件還是需要的的,下載地址:點我。如下圖一,是tess4j下載解壓的完整目錄,dist放的是tess4j的jar包,如果你用的是maven,就直接導入這個tess4j版本的jar,lib放的是windows執行的dll文件和開發中需要用的、依賴的jar包,tessdata是字庫,我這裏是純識別數字,所以沒有引入中文字庫。我用的是maven,所以就只需要lib和tessdata這個兩個文件夾。
二、獲取驗證碼圖片,識別圖片中的驗證碼,設置用戶名、密碼,保持會話進行登錄。
/**
*
* @param url 系統地址
* @param user 用戶名
* @param pwd 密碼
* @param tess4jpath tess4j的地址 如G:\test\Tess4J-3.4.8-src\Tess4J
* @return
*/
public Map login(String url, String user, String pwd,String tess4jpath) {
Map<String,String> map = null;
Connection.Response LoginResponse = null;
try {
LoginResponse = Jsoup.connect(url).method(Connection.Method.GET).execute();
map = LoginResponse.cookies();//獲取會話,登錄後需要保持會話
Document document = LoginResponse.parse();
Element element = document.getElementById("驗證碼圖片標籤");
String codeimgurl = url+element.attr("src");
String codeimgpath = tess4jpath+"\\codeimg";
//下載驗證碼圖片
byte[] codeimgdata = Jsoup.connect(codeimgurl).ignoreContentType(true).execute().bodyAsBytes();
FileUtils.saveImg(codeimgdata, codeimgpath, "codeimg.jpg");
//識別樣本輸出地址
String ocrResult = codeimgpath+"\\codetmpimgtmp.jpg";
String OriginalImg = codeimgpath+"\\codeimg.jpg";
//去噪點
FileUtils.removeBackground(OriginalImg, ocrResult);
ITesseract instance =new Tesseract();
instance.setDatapath(tess4jpath);
File imgDir =new File(ocrResult);
String code = instance.doOCR(imgDir);//識別驗證碼
System.out.println("code:"+code);
Map datas = new HashMap();
datas.put("user", user);
datas.put("pwd", pwd);
datas.put("ident",code);
Connection connection = Jsoup.connect(url+"/login?op=userLogin");
//輸入用戶名和密碼保持會話登錄
Connection.Response response1 = connection.data(datas).cookies(map).method(Connection.Method.POST).execute();
} catch (IOException e) {
map = null;
e.printStackTrace();
} catch (TesseractException e) {
map = null;
e.printStackTrace();
}finally {
return map;
}
}
上述代碼中使用的工具類
public class FileUtils {
/**
* 級聯創建目錄
* @param path
*/
public static void creatDir(String path) {
File file = new File(path);
if(!file.exists()) {
file.mkdirs();
}
}
/**
* 驗證碼圖片處理
* @param imgUrl
* @param resUrl
*/
public static void removeBackground(String imgUrl, String resUrl){
//定義一個臨界閾值
int threshold = 300;
try{
BufferedImage img = ImageIO.read(new File(imgUrl));
int width = img.getWidth();
int height = img.getHeight();
for(int i = 1;i < width;i++){
for (int x = 0; x < width; x++){
for (int y = 0; y < height; y++){
Color color = new Color(img.getRGB(x, y));
//System.out.println("red:"+color.getRed()+" | green:"+color.getGreen()+" | blue:"+color.getBlue());
int num = color.getRed()+color.getGreen()+color.getBlue();
if(num >= threshold){
img.setRGB(x, y, Color.WHITE.getRGB());
}
}
}
}
for(int i = 1;i<width;i++){
Color color1 = new Color(img.getRGB(i, 1));
int num1 = color1.getRed()+color1.getGreen()+color1.getBlue();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Color color = new Color(img.getRGB(x, y));
int num = color.getRed()+color.getGreen()+color.getBlue();
if(num==num1){
img.setRGB(x, y, Color.BLACK.getRGB());
}else{
img.setRGB(x, y, Color.WHITE.getRGB());
}
}
}
}
File file = new File(resUrl);
if (!file.exists())
{
File dir = file.getParentFile();
if (!dir.exists())
{
dir.mkdirs();
}
try
{
file.createNewFile();
}
catch (IOException e)
{
e.printStackTrace();
}
}
ImageIO.write(img, "jpg", file);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 保存文件
* @param imgdata
* @param filePath
* @param filename
*/
public static void saveImg(byte[] imgdata,String filePath,String filename) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
File dir = new File(filePath);
try {
if(!dir.exists()&&dir.isDirectory()) {
dir.mkdirs();
}
file = new File(filePath+File.separator+filename);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(imgdata);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(bos!=null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos!=null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意:並不是所有的系統的驗證碼都能識別,就拿這個12306來說,我也不知道怎麼識別,關鍵是我也不敢問啊!我識別的驗證碼是簡單的數字驗證碼,原始驗證碼 ,去噪的驗證碼 ,去噪後的驗證碼便於識別數字。
不管怎麼樣,希望你使用的Java爬蟲要用在正途上,畢竟最近也聽說了有爬蟲工程師被抓判刑的消息,所謂細水長流,我們要走可持續發展的道路。