1、概念
网页解析,即程序自动分析网页内容、获取信息,从而进一步处理信息。
htmlparser包提供方便、简洁的处理html文件的方法,它将html页面中的标签按树形结构解析成一个一个结点,一种类型的结点对应一个类,通过调用其方法可以轻松地访问标签中的内容。
(
官方文档:http://htmlparser.sourceforge.net/samples.html
API:http://htmlparser.sourceforge.net/javadoc/index.html
其它HTML 解释器:jsoup等。由于HtmlParser自2006年以后就再没更新,目前很多人推荐使用jsoup代替它。
)
2、使用HtmlPaser的关键步骤
1.用被提取的网页的url实例化一个Parser
2.实例化Filter,设置页面过滤条件——只获取<a>标签与<frame>标签的内容
3.用Parser提取页面中所有通过Filter的结点,得到NodeList
4.遍历NodeList,调用Node的相应方法得到其中的链接,加入子链接的集合
5.返回子链接集合
3、使用Parser的构造函数创建解释器
Parser() Zero argument constructor. |
Parser(Lexer lexer) Construct a parser using the provided lexer. |
Parser(Lexer lexer, ParserFeedback fb) Construct a parser using the provided lexer and feedback object. |
Parser(String resource) Creates a Parser object with the location of the resource (URL or file). |
Parser(String resource, ParserFeedback feedback) Creates a Parser object with the location of the resource (URL or file) You would typically create a DefaultHTMLParserFeedback object and pass it in. |
Parser(URLConnection connection) Construct a parser using the provided URLConnection. |
Parser(URLConnection connection, ParserFeedback fb) Constructor for custom HTTP access. |
对于大多数使用者来说,使用最多的是通过一个URLConnection或者一个保存有网页内容的字符串来初始化Parser,或者使用静态函数来生成一个Parser对象。
4、HtmlPaser使用Node对象保存各节点信息
AbstractNodes是Node的直接子类,也是一个抽象类。它的三个直接子类实现是RemarkNode,用于保存注释。在输出结果的toString部分中可以看到有一个"Rem (345[6,2],356[6,13]): 这是注释",就是一个RemarkNode。TextNode也很简单,就是用户可见的文字信息。TagNode是最复杂的,包含了HTML语言中的所有标签,而且可以扩展(扩展 HTMLParser 对自定义标签的处理能力)。TagNode包含两类,一类是简单的Tag,实际就是不能包含其他Tag的标签,只能做叶子节点。另一类是CompositeTag,就是可以包含其他Tag,是分支节点
HTMLParser遍历了网页的内容以后,以树(森林)结构保存了结果。HTMLParser访问结果内容的方法有两种。使用Filter和使用Visitor。
(1)访问各个节点的方法
Node getParent ():取得父节点
NodeList getChildren ():取得子节点的列表
Node getFirstChild ():取得第一个子节点
Node getLastChild ():取得最后一个子节点
Node getPreviousSibling ():取得前一个兄弟(不好意思,英文是兄弟姐妹,直译太麻烦而且不符合习惯,对不起女同胞了)
Node getNextSibling ():取得下一个兄弟节点
(2)取得Node内容的函数
String getText ():取得文本
String toPlainTextString():取得纯文本信息。
String toHtml () :取得HTML信息(原始HTML)
String toHtml (boolean verbatim):取得HTML信息(原始HTML)
String toString ():取得字符串信息(原始HTML)
Page getPage ():取得这个Node对应的Page对象
int getStartPosition ():取得这个Node在HTML页面中的起始位置
int getEndPosition ():取得这个Node在HTML页面中的结束位置
用于Filter过滤的函数:
void collectInto (NodeList list, NodeFilter filter):基于filter的条件对于这个节点进行过滤,符合条件的节点放到list中。
用于Visitor遍历的函数:
void accept (NodeVisitor visitor):对这个Node应用visitor
5、使用Filter访问Node节点及其内容
(1)Filter的种类
顾名思义,Filter就是对于结果进行过滤,取得需要的内容。
所有的Filter均实现了NodeFilter接口,此接口只有一个方法Boolean accept(Node node),用于确定某个节点是否属于此Filter过滤的范围。
HTMLParser在org.htmlparser.filters包之内一共定义了16个不同的Filter,也可以分为几类。
判断类Filter:
TagNameFilter
HasAttributeFilter
HasChildFilter
HasParentFilter
HasSiblingFilter
IsEqualFilter
逻辑运算Filter:
AndFilter
NotFilter
OrFilter
XorFilter
其他Filter:
NodeClassFilter
StringFilter
LinkStringFilter
LinkRegexFilter
RegexFilter
CssSelectorNodeFilter
除此以外,可以自定义一些Filter,用于完成特殊需求的过滤。
(2)Filter的使用示例
以下示例用于提取HTML文件中的链接
public class HtmlNodeParser { // http://www.sohu.com /** * 解析url地址中对应的页面中的 a标签与frame标签 * 过滤器为空表示全网爬 * @throws ParserException * @throws IOException */ public Set<String> parseLink( String url, NodeFilter filter) throws ParserException, IOException{ if( url.startsWith("http://")==false){ url="http://"+ url; } Set<String> set=new HashSet<String>(); // if( url.lastIndexOf("?")>-1){ // url=url+"&"; // }else{ // url=url+"?"; // } // url=url+"zy="+UUID.randomUUID().toString().substring(0,4); URL u=new URL( url ); HttpURLConnection con=(HttpURLConnection) u.openConnection(); con.setRequestProperty("Host", "download.csdn.net"); con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 5.1; rv:27.0) Gecko/20100101 Firefox/27.0"); con.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); con.connect(); String rm=con.getContentType(); //构造一个Parser,并设置相关的属性 Parser parser=new Parser( con ); String encoding= "utf-8"; if( rm.indexOf("charset")==-1){ parser.setEncoding( encoding ); } //自定义一个Filter,专门用于过滤frame标记,然后取得标签中的src属性值 NodeFilter framefilter=new NodeFilter(){ @Override public boolean accept(Node node) { if( node.getText().indexOf("frame src=")>=0 ){ return true; }else{ return false; } } }; // 创建第二个Filter,过滤<a>标签 LinkTag表示超链接的标记, OrFilter linkFilter=new OrFilter( new NodeClassFilter(LinkTag.class), framefilter ); NodeList list = parser.extractAllNodesThatMatch (linkFilter); //4、对取得的Node进行处理 for(int i=0;i<list.size();i++){ Node node=list.elementAt(i); String linkurl=null; //如果链接类型为<a /> if( node instanceof LinkTag ){ // href LinkTag linkTag=(LinkTag) node; linkurl=linkTag.getLink(); }else { //是frame节点 src frame src=xxxx.html width= String frame=node.getText(); int start=frame.indexOf("src="); frame=frame.substring( start ); int end= frame.indexOf(" "); if( end==-1){ end=frame.indexOf(">"); } linkurl=frame.substring(4, end); } if( linkurl==null || "".equals( linkurl) || !linkurl.startsWith("http://") ){ continue; } //accept( node)==>判断是否属于本次搜索范围的url if( filter!=null && filter.accept( node)==false ){ continue; } //System.out.println( "========>"+ node+"\t\t XXXXX:=> "+ linkurl ); set.add( linkurl ); } return set; }
程序中的一些说明:
(1)通过Node#getText()取得节点的String。
(2)node instanceof TagLink,即<a/>节点,其它还有很多的类似节点,如tableTag等,基本上每个常见的html标签均会对应一个tag。官方文档说明如下:
org.htmlparser.nodes | The nodes package has the concrete node implementations. |
org.htmlparser.tags | The tags package contains specific tags. |
其中用到的LinkFilter接口定义如下:
测试程序如下:
http://www.hao123.com
http://map.baidu.com/m?word=&fr=ps01000
六 visitor
HTMLParser遍历了网页的内容以后,以树(森林)结构保存了结果。HTMLParser访问结果内容的方法有两种。使用Filter和使用Visitor。
下面介绍使用Visitor访问内容的方法。
(下面是我在网上找的例子 先存起来 以后用再来看)
NodeVisitor
从简单方面的理解,Filter是根据某种条件过滤取出需要的Node再进行处理。Visitor则是遍历内容树的每一个节点,对于符合条件的节点进行处理。实际的结果异曲同工,两种不同的方法可以达到相同的结果。
下面是一个最常见的NodeVisitro的例子。
测试代码:
public static void main(String[] args) { try{ Parser parser = new Parser( (HttpURLConnection) (new URL("http://127.0.0.1:8080/HTMLParserTester.html")).openConnection() ); NodeVisitor visitor = new NodeVisitor( false, false ) { public void visitTag(Tag tag) { message("This is Tag:"+tag.getText()); } public void visitStringNode (Text string) { message("This is Text:"+string); } public void visitRemarkNode (Remark remark) { message("This is Remark:"+remark.getText()); } public void beginParsing () { message("beginParsing"); } public void visitEndTag (Tag tag){ message("visitEndTag:"+tag.getText()); } public void finishedParsing () { message("finishedParsing"); } }; parser.visitAllNodesWith(visitor); } catch( Exception e ) { e.printStackTrace(); } }