import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import org.htmlparser.Node;
import org.htmlparser.PrototypicalNodeFactory;
import org.htmlparser.Tag;
import org.htmlparser.lexer.Lexer;
import org.htmlparser.lexer.Page;
import org.htmlparser.nodes.TagNode;
import org.htmlparser.nodes.TextNode;
import org.htmlparser.tags.ScriptTag;
import org.htmlparser.tags.StyleTag;
import org.htmlparser.util.ParserException;
import com.lietu.tag.CnTagMaker.WordWeight;
public class TagExt {
private static StringBuffer body;
private static String title;
public static void main(String[] args) throws Exception {
String path;
if (0 >= args.length) {
path =
/** 以下是測試地址 * */
// "http://www.ibm.com/developerworks/cn/webservices/0901_haoxf_humantask/";
// "http://developers.sun.com.cn/Java/xref_index.html";
"http://mil.news.sina.com.cn/2009-01-09/0839538126.html";
// "http://www.sina.com.cn/";
// "http://www.ibm.com/";
// "http://hao861002.iteye.com/blog/301581";
} else {
path = args[0];
}
/**
*
* 構造 URL ,並打開網絡鏈接。
*
*/
URL url = new URL(path);
HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection();
/** 對該網頁進行解析 * */
parseHTML(httpUrl);
/** 打印解析後的內容 * */
System.out.println("body=" + body);
}
/**
*
* 解析網頁內容
*
* @param uc
* 傳入一個 HttpURLConnection 鏈接對象
*
* @throws ParserException
*
*/
public static void parseHTML(HttpURLConnection uc) throws ParserException {
/** 聲明節點 * */
Node node;
String stringText;
body = new StringBuffer();
/***********************************************************************
* 從 head 頭獲取網頁編碼格式。該方式取決於服務器是否設置 charSet 值,如果沒有,該
*
* 方式將無法獲取 charSet 值
**********************************************************************/
String contentType = uc.getContentType();
String charSet = getCharset (contentType);
Lexer lexer = null ;
if (charSet == null ) {
charSet = "UTF-8" ;
}
try {
lexer = new Lexer( new Page(uc.getInputStream(), charSet));
} catch (Exception e) {
e.printStackTrace();
return ;
}
/** 對網頁內容進行解析 * */
lexer.setNodeFactory( new PrototypicalNodeFactory());
/** 設置開關,決定網頁是否重新解析 * */
boolean tryAgain = false ;
while ( null != (node = lexer.nextNode())) {
/** 以下是判斷節點的類型,並作相應的處理 * */
if (node instanceof ScriptTag) {
while ( null != (node = lexer.nextNode())) {
if (node instanceof Tag) {
Tag tag = (Tag) node;
if (tag.isEndTag() && "SCRIPT" .equals(tag.getTagName())) {
break ;
}
}
}
if ( null == node)
break ;
} else if (node instanceof StyleTag) {
while ( null != (node = lexer.nextNode())) {
if (node instanceof Tag) {
Tag tag = (Tag) node;
if (tag.isEndTag())
break ;
}
}
if ( null == node)
break ;
} else if (node instanceof TextNode) {
stringText = node.toPlainTextString();
if ( "" .equals( title ))
continue
else if (node instanceof TextNode) {
stringText = node.toPlainTextString();
if ( "" .equals( title ))
continue ;
stringText = stringText.replaceAll( "[ \t\n\f\r ]+" , " " );
stringText = TextHtml.html2text (stringText.trim());
if (! "" .equals(stringText)) {
body .append(stringText);
body .append( " " );
}
} else if (node instanceof TagNode) {
TagNode tagNode = (TagNode) node;
String name = ((TagNode) node).getTagName();
if (name.equals( "TITLE" ) && !tagNode.isEndTag()) {
node = lexer.nextNode();
stringText = node.toPlainTextString().trim();
if (! "" .equals(stringText)) {
title = stringText;
}
} else if (name.equals( "META" )) {
String contentCharSet = tagNode.getAttribute( "CONTENT" );
// System.out.println("contentCharset="+contentCharSet);
int b = contentCharSet.toLowerCase().indexOf( "charset" );
if (b > -1) {
String newCharSet = getCharset (contentCharSet);
// System.out.println("newCharSet=" + newCharSet);
if (!charSet.equals(newCharSet)) {
tryAgain = true ;
charSet = newCharSet;
// System.out.println("charSet=" + charSet);
// System.out.println("newCharSet=" + newCharSet);
break ;
}
}
}
}
}
/***********************************************************************
* 如果在 Meta 信息中檢測到新的字符編碼,則需要按照 meta 信息中的編碼再次解析網頁 。
**********************************************************************/
if (tryAgain) {
body = new StringBuffer();
try {
uc = (HttpURLConnection) uc.getURL().openConnection();
lexer = new Lexer( new Page(uc.getInputStream(), charSet));
} catch (Exception e) {
e.printStackTrace();
}
lexer.setNodeFactory( new PrototypicalNodeFactory());
while ( null != (node = lexer.nextNode())) {
if (node instanceof TextNode) {
stringText = node.toPlainTextString();
if ( "" .equals( title ))
continue ;
stringText = stringText.replaceAll( "[ \t\n\f\r ]+" , " " );
stringText = TextHtml.html2text (stringText.trim());
if (! "" .equals(stringText)) {
body .append(stringText);
body .append( " " );
}
}
}
}
}
/**
*
* 找出最終的網頁編碼
*
* @param name
* 經過 getCharset 方法處理後 meta 標籤的值
*
* @param _default
* 默認的編碼集
*
* @return
*
*/
public static String findCharset(String name, String _default) {
String ret;
try {
Class<java.nio.charset.Charset> cls;
Method method;
Object object;
cls = java.nio.charset.Charset.class;
method = cls.getMethod("forName", new Class[] { String.class });
object = method.invoke(null, new Object[] { name });
method = cls.getMethod("name", new Class[] {});
object = method.invoke(object, new Object[] {});
ret = (String) object;
} catch (NoSuchMethodException nsme) {
ret = name;
} catch (IllegalAccessException ia) {
ret = name;
} catch (InvocationTargetException ita) {
ret = _default;
System.out
.println("unable to determine cannonical charset name for "
+ name + " - using " + _default);
}
return (ret);
}
/**
*
* 處理 meta 中的內容,並調用 findCharset() 方法獲取編碼值
*
* @param content
* Meta 中的內容
*
* @return
*
*/
public static String getCharset(String content) {
final String CHARSET_STRING = "charset";
int index;
String ret;
ret = null;
if (null != content) {
index = content.indexOf(CHARSET_STRING);
if (index != -1) {
content = content.substring(index + CHARSET_STRING.length())
.trim();
if (content.startsWith("=")) {
content = content.substring(1).trim();
index = content.indexOf(";");
if (index != -1)
content = content.substring(0, index);
if (content.startsWith("\"") && content.endsWith("\"")
&& (1 < content.length()))
content = content.substring(1, content.length() - 1);
if (content.startsWith("'") && content.endsWith("'")
&& (1 < content.length()))
content = content.substring(1, content.length() - 1);
ret = findCharset(content, ret);
}
}
}
return (ret);
}
}
說明:
該類使用 Boolean 型 tryAgain 來判斷是否進行第二次解析!
如果通過 String contentType = uc.getContentType() 來獲得 http 協議中 Head 頭中contentType 中的值,並使用 String charSet = getCharset(contentType); 從 contentType 中獲取charSet 值。如果此時 charSet 獲取的爲空,則先用 charSet=UTF-8 (即:默認情況下, charSet 值爲 UTF-8 )進行解析。使用 org.htmlparser.tags 包中的類對比節點 node ,並對合適的節點的內容進行處理,本例中是對類型爲 TextNode 的 node 節點,獲取其中的內容,並將其添加到類型爲StringBuffer 的 body 當中。當解析到 <meta> 標籤的時候,會試圖從 <meta> 中獲取 newCharSet 值,並將 newCharSet 和 charSet 進行比較,如果相同,則不再進行解析,主函數將 body 打印。如果不同,說明默認的的 charSet 值並不是網頁的編碼格式,剛纔的解析可能不正確,會出現亂碼。這時候,將 newCharSet 中的值賦給 charSet ,並將 tryAgain 值設爲 true 。當程序進行到 if(tryAgain){ …… } 判斷時,結果爲真,則根據新得到的 charSet 值進行二次解析。這時候,我們就會得到正確的解析結果。