君子博学而日参省乎己 则知明而行无过矣

博客园 首页 新随笔 联系 订阅 管理

参考了iteye上的一篇文章http://yshjava.iteye.com/blog/1528208,本人还原了该文章的源码,还请那位博主原谅

精确抽取web网页内容一直是做数据抽取开发比较头痛的问题,目前国内外也有很多研究成果,尚待进一步发掘并应用于实际项目中

下面是转载部分:

应该说,在WEB分块领域,已经有大量的研究工作。由于HTML语法的灵活性,目前大部分的网页都没有完全遵循W3C规范,这样可能会导致DOM树结果的错误。更重要的是,DOM树最早引入是为了在浏览器中进行布局显示,而不是进行WEB页面的语义结构描述。某些文献中提到,根据标签把网页分成若干内容块,这些分块方法流程简单,但面对日益复杂的网页和不规范的网页结果,其分块效果往往不能令人满意。

另一类方法从视觉特征对页面结构进行挖掘。典型的代表就是微软亚洲研究院提出的VIPS(Vision-based Page Segmentation)。它利用WEB页面的视觉提示,如背景颜色、字体颜色和大小、边框、逻辑块和逻辑块之间的间距等,结合DOM树进行语义分块,并把它应用在了TREC2003的评测中,取得较好的效果。但是由于视觉特征的复杂性,如何保证规则集的一致性是一大难点,另外VIPS算法需要计算和保存DOM树中所有节点的视觉信息,导致该算法在时间和内存上的消耗比较大,使得在处理含有大量DOM节点的网页时性能不高。

利用网页的视觉特征和DOM树的结构特性对网页进行分块,并采用逐层分块逐层删减的方法将与正文无关的噪音块删除,从而得到正文块。对得到的正文块运用VIPS算法得到完整的语义块,最后在语义块的基础上提取正文内容。试验表明,这种方法是切实可行的。

文本网页可分为两种类型:主题型网页、目录型网页。主题型网页通常通过成段的文字描述一个活多个主题,虽然主体性网页也会出现图片和超链接,但是这些图片和超链接并不是网页的主体。目录型网页通常提供一组相关或者不相关的连接,本文所研究的正文信息提取指的是针对新闻类主题型网页中核心文本的提取,理论上来讲,应用本文所述研究成果,将算法稍加改造,将同样适用于论坛类主题型网页的正文提取。

分块统计算法

该算法中首先将DOM树中的节点类型分为四类,分别是容器类、文本类、移除类和图片类。 

/**
     * 检查指定DOM节点是否为容器类节点
     * @param node
     * @return 
     */protectedboolean checkContainerType(DomNode node){return(node instanceofHtmlUnknownElement|| node instanceofHtmlFont|| node instanceofHtmlListItem|| node instanceofHtmlUnorderedList|| node instanceofHtmlDivision|| node instanceofHtmlCenter|| node instanceofHtmlTable|| node instanceofHtmlTableBody|| node instanceofHtmlTableRow|| node instanceofHtmlTableDataCell|| node instanceofHtmlForm);}/**
     * 检查指定DOM节点是否为文本类节点
     * @param node
     * @return 
     */protectedboolean checkTextType(DomNode node){return(node instanceofHtmlSpan|| node instanceofDomText|| node instanceofHtmlParagraph);}/**
     * 检查指定DOM节点是否为移除类节点
     * @param node
     * @return 
     */protectedboolean checkRemoveType(DomNode node){return(node instanceofHtmlNoScript|| node instanceofHtmlScript|| node instanceofHtmlInlineFrame|| node instanceofHtmlObject|| node instanceofHtmlStyle|| node instanceofDomComment);}/**
     * 检查指定DOM节点是否为图片类节点
     * @param node
     * @return 
     */protectedboolean checkImageType(DomNode node){return(node instanceofHtmlImage);}

算法将文本类节点作为探测的直接对象,在探测过程中遇到移除类节点时直接移除,遇到图片类节点时不做统计,直接跳过,但是不会移除该类节点,遇到容器类节点时,直接探测其子节点。 

/**
     * 检查指定节点是否含有容器类节点,如果有则继续递归跟踪,否则探测该节点文本数据
     * @param nodeList
     * @return 是否含有容器类节点
     */protectedboolean checkContentNode(List<DomNode> nodeList){boolean hasContainerNode =false;if(nodeList !=null){for(DomNode node : nodeList){if(checkRemoveType(node)){
                    node.remove();}elseif(checkContainerType(node)){List<DomNode> list = node.getChildNodes();if(list !=null){
                        hasContainerNode =true;if(!this.checkContentNode(list)){if(cleanUpDomNode(node)){
                                checkNode(node);}}}}else{if(node.isDisplayed()&& checkTextType(node)){
                        checkNode(node);}else{
                        cleanUpDomNode(node);}}}}return hasContainerNode;}/**
     * 清理指定节点内的无效节点
     * @param element
     * @return 该节点是否有效
     */protectedboolean cleanUpDomNode(DomNode element){if(element ==null){returnfalse;}List<DomNode> list = element.getChildNodes();int linkTextLength =0;boolean flag =false;if(list !=null){for(DomNode node : list){if(checkTextType(node)){continue;}elseif(checkRemoveType(node)){
                    node.remove();
                    flag =true;}elseif(checkImageType(node)){//图片类型节点暂时不作处理}elseif(node instanceofHtmlAnchor){String temp = node.asText();
                    temp = encoder.encodeHtml(temp);int length =Chinese.chineseLength(temp.trim());if(length >0){
                        linkTextLength += length;}}elseif(checkContainerType(node)){if(!cleanUpDomNode(node)){
                        node.remove();}
                    flag =true;}}}String content = element.asText();
        content = encoder.encodeHtml(content);return(flag ||Chinese.chineseLength(content.trim())- linkTextLength >50||(content.trim().length()-Chinese.chineseLength(content)>5&&!flag));}

当算法首次探测到有效文本块时,记录该文本块的父节点块,并推测其为该网页正文文本所在区域,稍后发现可能性更高的块时,直接替换前一个推测为网页正文区域的块。 

/**
     * 推测正文文本区域信息
     * @param node 
     */protectedvoid checkNode(DomNode node){String temp = node.getTextContent();
        temp = encoder.encodeHtml(temp);int length =Chinese.chineseLength(temp.trim());
        temp =null;if(contentNode !=null&& contentNode.equals(node.getParentNode())){
            maxLength += length;}elseif(length > maxLength){
            maxLength = length;
            contentNode = node.getParentNode();}}

这样的逻辑适合于新闻、博客等整个网页中只有一个文本区域的主题型网页,而不适合与论坛、贴吧等网页中有多个兄弟文本区域的主题型网页。这里可以稍加改造,对探测到的所有文本块,及其所在区域做一个联合统计和推测,即可适用于论坛主题的网页。

该步骤采用递归探测,探测结果为0个或1个DOM节点(如果是论坛类主题页面,识别结果就会是N个DOM节点)。 

/**
     * 在N个DOM节点中搜索正文文本所在的节点
     * @param nodeList
     * @return 
     */publicDomNode searchContentNode(List<DomNode> nodeList){
        checkContentNode(nodeList);if(cleanUpDomNode(contentNode)){return contentNode;}else{returnnull;}}

机器学习

当智能识别模块识别某个网页正文文本成功时,其将自动保存该网页的相关信息和探测结果数据(目前仅仅记录探测到的正文区域的XPath,稍后可能会保存一个支持序列化和反序列化的对象来存储更多探测结果),下次再探测该网页或探测来自该网页所在domain的其他网页时,将首选从知识库中取出之前的经验,来抽取正文,如果抽取失败,则继续尝试智能识别。目前每一个domain只支持保存一条经验,但是稍后可以扩展成一个列表,并按照其成功应用的次数来排序,甚至于在该模块内添加一个过滤链,在智能识别前首先将任务通过过滤链,如果在过滤链内根据以往经验抽取成功,则直接返回,否则再由智能识别模块探测正文文本。将来也有可能会将机器学习和人工指导结合起来,既可以自主积累知识,也可以从数据源中加载认为添加的知识。 

以下代码并不能构成真正的机器学习,实际上,这里仅仅只做了一个演示。

/**
     * 在网页中搜索正文文本
     * @param html
     * @return 识别成功后,将封装一个PageData对象返回<br/>
     * 之所以返回一个对象,是考虑到将来可能加强该算法后,能够从页面中抽取更多数据,PageData对象能够更好的封装这些结果
     */publicstaticPageData spotPage(HtmlPage html){if(html ==null){returnnull;}String domain =URLHelper.getDomainByUrl(html.getUrl());String contentXPath = contentXPathMap.get(domain);PageData pageData =null;HtmlEncoder encoder =HtmlEncoderFactory.createHtmlEncoder(html.getPageEncoding());AutoSpot2_2 spoter =newAutoSpot2_2(encoder);List<DomNode> nodeList =null;if(contentXPath !=null){
            log.debug("在经验库中找到站点["+ domain +"]的相关数据,正在尝试应用. . .");
            nodeList =(List<DomNode>) html.getByXPath(contentXPath);}else{
            log.debug("第一次遇到站点["+ domain +"],正在尝试智能识别. . .");}if(nodeList ==null|| nodeList.isEmpty()){
            nodeList = html.getBody().getChildNodes();
            log.debug("经验库中关于站点["+ domain +"]的相关数据无法应用于当前网页,正在尝试重新识别. . .");}if(nodeList !=null){DomNode node = spoter.searchContentNode(nodeList);if(node !=null){
                pageData =newPageData();
                pageData.setContent(encoder.encodeHtml(node.asXml()));
                pageData.setTitle(html.getTitleText());
                contentXPathMap.put(domain, node.getCanonicalXPath());}}return pageData;}

基于分块统计和机器学习的主题类网页内容识别算法实现和应用范例


基于分块统计和机器学习的主题类网页内容识别算法实现和应用范例

就目前而言,该算法的探测结果还是勉强能够让人接受的,通过对来自百度新闻的新闻页面进行跟踪和识别,识别成功率超过90%,准确率则低很多,不到80%。

posted on 2013-05-16 02:52  刺猬的温驯  阅读(1110)  评论(3编辑  收藏  举报