近日在做爬蟲功能,爬取網頁內容,然后對內容進行語義分析,最后對網頁打標簽,從而判斷訪問該網頁的用戶的屬性。
在爬取內容時,遇到亂碼問題。故需對網頁內容編碼格式做判斷,方式大體分為三種:一、從header標簽中獲取Content-Type=#Charset;二、從meta標簽中獲取Content-Type=#Charset;三、根據頁面內容分析編碼格式。
其中一/二方式並不能准確指示該頁面的具體編碼方式,周全考慮,加入第三種方式。
第三種方式引入開源jar包info.monitorenter.cpdetector,可以從github上面下載(https://github.com/onilton/cpdetector-maven-repo/tree/master/info/monitorenter/cpdetector/1.0.10)下載。
package com.mobivans.encoding; import info.monitorenter.cpdetector.io.ASCIIDetector; import info.monitorenter.cpdetector.io.ByteOrderMarkDetector; import info.monitorenter.cpdetector.io.CodepageDetectorProxy; import info.monitorenter.cpdetector.io.JChardetFacade; import info.monitorenter.cpdetector.io.ParsingDetector; import info.monitorenter.cpdetector.io.UnicodeDetector; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.Charset; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; public class PageEncoding { /** 測試用例 * @param args */ public static void main(String[] args) { // String charset = getEncodingByHeader("http://blog.csdn.net/liuzhenwen/article/details/4060922"); // String charset = getEncodingByMeta("http://blog.csdn.net/liuzhenwen/article/details/4060922"); String charset = getEncodingByContentStream("http://blog.csdn.net/liuzhenwen/article/details/5930910"); System.out.println(charset); } /** * 從header中獲取頁面編碼 * @param strUrl * @return */ public static String getEncodingByHeader(String strUrl){ String charset = null; try { URLConnection urlConn = new URL(strUrl).openConnection(); // 獲取鏈接的header Map<String, List<String>> headerFields = urlConn.getHeaderFields(); // 判斷headers中是否存在Content-Type if(headerFields.containsKey("Content-Type")){ //拿到header 中的 Content-Type :[text/html; charset=utf-8] List<String> attrs = headerFields.get("Content-Type"); String[] as = attrs.get(0).split(";"); for (String att : as) { if(att.contains("charset")){ // System.out.println(att.split("=")[1]); charset = att.split("=")[1]; } } } return charset; } catch (MalformedURLException e) { e.printStackTrace(); return charset; } catch (IOException e) { e.printStackTrace(); return charset; } } /** * 從meta中獲取頁面編碼 * @param strUrl * @return */ public static String getEncodingByMeta(String strUrl){ String charset = null; try { URLConnection urlConn = new URL(strUrl).openConnection(); //避免被拒絕 urlConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"); // 將html讀取成行,放入list List<String> lines = IOUtils.readLines(urlConn.getInputStream()); for (String line : lines) { if(line.contains("http-equiv") && line.contains("charset")){ // System.out.println(line); String tmp = line.split(";")[1]; charset = tmp.substring(tmp.indexOf("=")+1, tmp.indexOf("\"")); }else{ continue; } } return charset; } catch (MalformedURLException e) { e.printStackTrace(); return charset; } catch (IOException e) { e.printStackTrace(); return charset; } } /** * 根據網頁內容獲取頁面編碼 * case : 適用於可以直接讀取網頁的情況(例外情況:一些博客網站禁止不帶User-Agent信息的訪問請求) * @param url * @return */ public static String getEncodingByContentUrl(String url) { CodepageDetectorProxy cdp = CodepageDetectorProxy.getInstance(); cdp.add(JChardetFacade.getInstance());// 依賴jar包 :antlr.jar & chardet.jar cdp.add(ASCIIDetector.getInstance()); cdp.add(UnicodeDetector.getInstance()); cdp.add(new ParsingDetector(false)); cdp.add(new ByteOrderMarkDetector()); Charset charset = null; try { charset = cdp.detectCodepage(new URL(url)); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println(charset); return charset == null ? null : charset.name().toLowerCase(); } /** * 根據網頁內容獲取頁面編碼 * case : 適用於不可以直接讀取網頁的情況,通過將該網頁轉換為支持mark的輸入流,然后解析編碼 * @param strUrl * @return */ public static String getEncodingByContentStream(String strUrl) { Charset charset = null; try { URLConnection urlConn = new URL(strUrl).openConnection(); //打開鏈接,加上User-Agent,避免被拒絕 urlConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"); //解析頁面內容 CodepageDetectorProxy cdp = CodepageDetectorProxy.getInstance(); cdp.add(JChardetFacade.getInstance());// 依賴jar包 :antlr.jar & chardet.jar cdp.add(ASCIIDetector.getInstance()); cdp.add(UnicodeDetector.getInstance()); cdp.add(new ParsingDetector(false)); cdp.add(new ByteOrderMarkDetector()); InputStream in = urlConn.getInputStream(); ByteArrayInputStream bais = new ByteArrayInputStream(IOUtils.toByteArray(in)); // detectCodepage(InputStream in, int length) 只支持可以mark的InputStream charset = cdp.detectCodepage(bais, 2147483647); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return charset == null ? null : charset.name().toLowerCase(); } }
注意的點:
1.info.monitorenter.cpdetector未在mvn-repository中開源,因而無法從mvn-repository中下載,需要將該jar下到本地,然后手動導入到本地repository,mvn命令如下:
mvn install:install-file -Dfile=jar包的位置 -DgroupId=該jar的groupId -DartifactId=該jar的artifactId -Dversion=該jar的version -Dpackaging=jar
然后在pom.xml中添加該jar的依賴
<!-- charset detector --> <dependency> <groupId>info.monitorenter.cpdetector</groupId> <artifactId>cpdetector</artifactId> <version>1.0.10</version> </dependency>
2.JChardetFacade.getInstance()在引入antlr.jar和chardet.jar之前會報異常,在pom.xml中添加這兩個jar的dependency:
<!-- antlr --> <dependency> <groupId>antlr</groupId> <artifactId>antlr</artifactId> <version>2.7.7</version> </dependency> <!-- ChardetFacade --> <dependency> <groupId>net.sourceforge.jchardet</groupId> <artifactId>jchardet</artifactId> <version>1.0</version> </dependency>
如果是普通項目則無需關心pom.xml,直接把這三個jar包下載下來然后添加到該項目的環境中即可