htmlparser文档.docx
《htmlparser文档.docx》由会员分享,可在线阅读,更多相关《htmlparser文档.docx(31页珍藏版)》请在冰豆网上搜索。
![htmlparser文档.docx](https://file1.bdocx.com/fileroot1/2023-4/18/69f616e5-43b5-4e24-9d65-78e9f6e657d2/69f616e5-43b5-4e24-9d65-78e9f6e657d21.gif)
htmlparser文档
htmlParser简单介绍
一,数据组织分析:
HtmlParser主要靠Node、AbstractNode和Tag来表达Html,因为Remark和Text相对简单,此处就将其忽略了。
Node是形成树结构表示HTML的基础,所有的数据表示都是接口Node的实现,Node定义了与页面树结构所表达的页面Page对象,定义了获取父、子、兄弟节点的方法,定义了节点到对应html文本的方法,定义了该节点对应的起止位置,定义了过滤方法,定义了Visitor访问机制。
AbstractNode是Node的一种具体的类实现,起到构成树形结构的作用,除了同具体Node相关的accetp方法,toString,toHtml,toPlainTextString方法以外,AbstractNode实现了大多基本的方法,使得它的子类,不用理会具体的树操作。
Tag是具体分析的主要内容。
Tag分成composite的Tag和不能包含其他Tag的简单Tag两类,其中前者的基类是CompositeTag,其子类包含BodyTag,Div,FrameSetTag,OptionTag,等27个子类;而简单Tag有BaseHrefTag、DoctypeTag,FrameTag,ImageTag,InputTag,JspTag,MetaTag,ProcessingInstructionTag这八类。
Node分成三类:
RemarkNode:
代表Html中的注释
TagNode:
标签节点,是种类最多的节点类型,上述Tag的具体节点类都是TagNode的实现。
TextNode:
文本节点
二,Visitor方式访问Html:
1,整体解析过程
用一个URL或页面String做一个Parser
用这个Parser做一个Visitor
使用Parser.visitAllNodeWith(Visitor)来遍历节点
获取Visitor遍历后得到的数据
2,Visit过程
做解析之前做的事情:
visitor.beginParsing();
每次取到一个节点Node,让该Node接受accept该Visitor
做解析后做的事情:
visitor.finishedParsing();
3,获取节点的过程:
逐步遍历Html,分析出Node。
此部分较为复杂,且对于我们应用来说无需很多了解,暂跳过。
4,节点访问
节点访问采用Visitor模式,Node的accept方法和具体Visitor的visit方法是关键。
首先三类Node来accept的方式各不相同:
对于所有TagNode都使用一个accept方法,即TagNode的accept方法。
首先判断是否是标签结尾,如果是就visitor.visitEndTag(this);否则visitor.visitTag(this);
如果是TextNode,那就visitor.visitStringNode(this);就可以了。
如果是RemarkNode,那就visitor.visitRemarkNode(this);就可以了。
实际上NodeVisitor里边这四种visit方法都是空的,因为在不同的Visitor中对于这三类节点的处理是不同的;对于需要处理的节点,只要重载对应的visit方法就行了,如果不处理那就不理会就可以了;另外,如果用户用自己的Visitor,那么还可以灵活的处理不同类型的节点了。
系统为我们实现了下面我要介绍的8种Visitor,实际上可以看作是系统给我们演示了如何做各种各样的Visitor来访问Html,因为实际上我们要真正来用HtmlParser的话,还需要特定的Visitor,而通过简单的这些系统提供的Visitor组合是难以做成什么事情的。
三,系统Visitor功能简介:
ObjectFindingVisitor:
用来找出所有指定类型的节点,采用getTags()来获取结果。
StringBean:
用来从一个指定的URL获取移除了和
之间代码的Html代码,也可以用做Visitor,用来移除这两种标签内部的代码,采用StringBean.getStrings()来获取结果。
HtmlPage:
提取Title,body中的节点和页面中的TableTag节点。
LinkFindingVisitor:
找出节点中包含某个链接的总个数。
StringFindingVisitor:
找出遍历的TextNode中含有指定字符串的个数。
TagFindingVisitor:
找出指定Tag的所有节点,可以指定多种类型。
TextExtractingVisitor:
从网页中把所有标签去掉来提取文本,这个提取文本的Visitor有时是很实用的,只是注意在提取文本时将标签的属性也去掉了,也就是说只剩下标签之间的文本,例如中的链接也去掉了。
UrlModifyingVisitor:
用来修改网页中的链接。
四,Filter
如果说visitor是遍历提取信息,当然这个信息可以包括某些节点或者从节点分析出来的更有效的信息,这都取决于我们的Visitor做成什么样子,那么Filter则目标很明确,就是用来提取节点的。
所以说要想用HtmlParser,首先要熟悉上面讲到的数据组织。
系统定义了17种具体的Filter,包括依据节点父子关系的Filter,连接Filter组合的Filter,依据网页内容匹配情况的filter,等等。
我们也可以implementFilter来做自己的Filter来提取节点。
Filter的调用是同Visitor独立的,因为也无需先filter出一些NodeList,再用Visitor来访问。
调用Filter的方法是:
NodeListnodeList=myParser.parse(someFilter);
解析之后,我们可以采用:
Node[]nodes=nodeList.toNodeArray();
来获取节点数组,也可以直接访问:
Nodenode=nodeList.elementAt(i)来获取Node。
另外,在Filter后得到NodeList以后,我们仍然可以使用NodeList的extractAllNodesThatMatch(someFilter)来进一步过滤,同时又可以用NodeList的isitAllNodesWith(someVisitor)来做进一步的访问。
这样,我们可以看到HtmlParser为我们提供了非常方便的Html解析方式,针对不同的应用可以采用visitor来遍历Html节点提取数据,也可以用Filter来过滤节点,提取出我们所关注的节点,再对节点进行处理。
通过这样的组合,一定能够找出我们所需要的信息。
HTMLParser解析HTML实例
开发中经常会用到对html文件的解析,特别是在做爬虫的时候,更是会很频繁的用到对html的解析
htmlparser工具包,提供了对html进行解析的便利工程方法
下在是使用htmlparser对html进行解析的实例.
importorg.htmlparser.Attribute;
importorg.htmlparser.Node;
importorg.htmlparser.Parser;
importorg.htmlparser.Tag;
importorg.htmlparser.tags.ScriptTag;
importorg.htmlparser.util.NodeList;
importorg.htmlparser.util.ParserException;
publicclassdfdasfads
{
publicstaticvoidmain(String[]args)throwsParserException
{
StringBuffersb=newStringBuffer();
sb.append(""p1\">"s2\"name=\"s22\">vvv
\r\n
.>\r\n
..>");
System.out.println(newdfdasfads().getFilterBody(sb.toString()));
}
publicStringgetFilterBody(StringstrBody)
{
//一、htmlparser处理提交
Parserparser=Parser.createParser(strBody,"utf-8");
NodeListlist;
StringreValue=strBody;
try
{
list=parser.parse(null);
visitNodeList(list);
reValue=list.toHtml();
}catch(ParserExceptione1)
{
e1.printStackTrace();
}
returnreValue;
}
/***//**
*移除所有标签的Id属性
*@paramlist标签集合
*/
privatevoidvisitNodeList(NodeListlist)
{
System.out.println(">>>visitNodeList(list)");
for(inti=0;i{
Nodenode=list.elementAt(i);
if(nodeinstanceofScriptTag)
{
list.remove(i);
continue;
}elseif(nodeinstanceofTag)
{
Tag_tag=(Tag)node;
_tag.removeAttribute("id");
}
NodeListchildren=node.getChildren();
if(children!
=null&&children.size()>0)
{
visitNodeList(children);
}
}
System.out.println("<<");
}
}
FilterBody.java
importorg.htmlparser.Attribute;
importorg.htmlparser.Node;
importorg.htmlparser.Parser;
importorg.htmlparser.Tag;
importorg.htmlparser.tags.ScriptTag;
importorg.htmlparser.util.NodeList;
importorg.htmlparser.util.ParserException;
publicclassFilterBody
{
publicstaticvoidmain(String[]args)throwsParserException
{
Stringsttt=""true\"src=\"quality=\"high\"bgcolor=\"#ffffff\"width=\"424\"height=\"404\"name=\"vsplayer\"align=\"middle\"type=\"application/x-shockwave-flash\"pluginspage=\"/>";
System.out.println(newFilterBody().getFilterBody(sttt));
}
publicStringgetFilterBody(StringstrBody)
{
//一、htmlparser处理提交
Parserparser=Parser.createParser(strBody,"utf-8");
NodeListlist;
StringreValue=strBody;
try
{
list=parser.parse(null);
visitNodeList(list);
reValue=list.toHtml();
}catch(ParserExceptione1)
{
}
returnreValue;
}
//递归
privatevoidvisitNodeList(NodeListlist)
{
for(inti=0;i{
Nodenode=list.elementAt(i);
if(nodeinstanceofTag)
{
if(nodeinstanceofScriptTag)
{
list.remove(i);
continue;
}
Tag_tag=(Tag)node;
_tag.removeAttribute("id");
_tag.removeAttribute("onload");
_tag.removeAttribute("alt");
StringtagName=_tag.getTagName();
if(tagName==null)
tagName="";
else
tagName=tagName.trim().toUpperCase();
visitTag((Tag)node);
}
NodeListchildren=node.getChildren();
if(children!
=null&&children.size()>0)
visitNodeList(children);
}
}
//获取tag
privatevoidvisitTag(Tagtag)
{
StringtagName=tag.getTagName();
if(tagName!
=null&&tagName.equalsIgnoreCase("embed"))
{
tag.setEmptyXmlTag(false);
tag.setAttribute("AllowNetworking","\"none\"");
tag.setAttribute("AllowScriptAccess","never",'"');
tag.setEmptyXmlTag(true);
}
Vectorattrs=tag.getAttributesEx();
for(inti=0;i{
Objectobj=attrs.elementAt(i);
if(obj!
=null&&objinstanceofAttribute)
{
visitAttribute((Attribute)obj,tag);
}
}
}
//获取tag属性
privatevoidvisitAttribute(Attributeattribute,Tagtag)
{
StringattName=attribute.getName();
if(attName==null)
attName="";
else
attName=attName.trim().toLowerCase();
StringtagName=tag.getTagName();
if(tagName==null)
tagName="";
else
tagName=tagName.trim().toLowerCase();
StringtagValue=tag.getText();
if(tagValue==null)
tagValue="";
else
tagValue=tagValue.trim().toLowerCase();
StringattribValue=attribute.getValue();
if(attribValue==null)
attribValue="";
else
attribValue=attribValue.trim().toLowerCase();
}
}
我这次要介绍的是如何抽取正文,这部分是最为核心的.因为如果不能很好的提取原有文章的内容和样式,那么搜索出来的东西
就会惨不忍睹.根本就没有使用价值
在做正文抽取模块之前我曾经参考过很多抽取模式,有配置模版的,有搞视觉匹配的.有搞关键字识别的.我挨个做了分析
首先配置摸版是不太现实的,因为我在搜索技术资讯的时候,根本不知道会搜索到哪个网站,也根本没精力去配置摸版.所以这个行不通
基于视觉效果的分析,这个难度比较大,而且只适合于规范的网站,而现在很多网站根本不规范,广告链接漫天飞.人家都把最好的
位置留给广告了.而且我一直怀疑这个模式的可行性,它只是一个善意的推测.所以这方面没做过多尝试
我在想,是否有种简单的方法呢?
难道就没有什么共性吗?
我想所有的正文应该有个共同的特点,那就是正文的长度应该超过其他文字组合的长度.很少会有一句话的正文,很少会有长度
短于标题的正文.所以这个应该成为一个突破口.
接下来,有一个很重要的问题,那段最长的正文在哪里呢?
肯定是在一个TABLE,或者DIV,或者ParagraphTag里.那好,那就找到那个包含文字最多的DIV或者TABLE.
不过问题又来了,HTML页面,经常是HTML元素的长度超过了正文的长度,有时候混入了不少的JAVASCRIPT.这些元素
HTMLPARSER经常会误认为是正文加以识别,导致很多正文竟然是一段JAVASCRIPT.
祛除杂质是一个关键,这里面要把那些HTML中常用的标签,以及连接中正文去除掉,否则,你搜索出来的很可能是别的什么,尤其
当正文文字相对较少的时候.我在搜索SOHU页面的时候就经常遇到这个问题,原因是SOHU的页面不是严格按照DIV布局,里面有很多广告
的JAVASCRIPT。
新浪的有些页面也有这个现象,反到是一些中小网站的布局很规范,呵呵,真奇怪了。
做完这些工作后,我发现仍然有些网页不能正常抓取,原因是HTMLPARSER对TEXT的认识有问题.例如一段文字在
ParagraphTag中或者span中包含的,就不能很好的识别.所以要单独做个抽取ParagraphTag内容的函数.
做完这些步骤后,有一个问题出来了就是正文中包含的图片,连接,粗体,正常的表格.这些问题一个个的冒出来.既然问题出来了
那就要一个个的解决.解决了这些难题.我的网站抓取文章的质量就大大的提高了85%的准确率,基本达到实用阶段.我网站上的正文快照基本和原文保持一致.
提供几个例子,大家可以看下原文和我抓取的有多少不同
1.
这个是单纯获取正文的例子,其中有粗体标签和链接
2
这个是正文里混有图片和表格.
我把抽取正文的部分代码和大家共享.这些代码基本解决了我在上面列举出来的问题。
包括正文中混有图片,连接,粗体,表格等。
大家要是有兴趣可以改造下这些代码请大家重点看protectedListextractHtml(NodenodeP,PageContextcontext,StringsiteUrl)
这个函数是正文抽取的入口。
我的这些函数写的不是很规范,别笑话!
/**
*收集HTML页面信息调用抓取函数按照自己的摸版生成网页
*@paramurl
*@paramurlEncode
*/
pu