当前位置: 代码迷 >> HTML/CSS >> Html parser 代码汇总 1
  详细解决方案

Html parser 代码汇总 1

热度:522   发布时间:2012-11-21 08:23:25.0
Html parser 代码集锦 1
nekohtml使用笔记

1、透明地创建HTML解析器
  利用Xerces2.0为基础,应用程序通过JAXP实例化解析器对象时,可以透明地创建HTML解析器,此时只需要将NekoHTML的jar文件,在CLASSPATH中放在Xerces的jar文件之前即可。nekohtmlXni.jar中的META-INF/services/org.apache.xerces.xni.parser.XMLParserConfiguration文件会被Xerces的读取并取代标准的设置文件,此处org.apache.xerces.xni.parser.XMLParserConfiguration文件的内容就是一个字符串"org.cyberneko.html.HTMLConfiguration"。这种方法的好处是简单透明,缺点是影响了Xerces在其它情况下的使用。
2、便利的HTML解析器类
  要想避免上述的问题,可以使用org.cyberneko.html.parsers包的DOM和SAX解析器类来创建解析器,这两个类都使用了HTMLConfiguration类。解析器一旦创建之后,就可以解析HTML文件,并用标准的XML接口来访问文件中的信息,就象面对的是一个XML文件一样。

3、文档片段解析
  除了DOM和SAX类,NekoHTML还提供了一个实验性质的DOMFragmentParser类,用以解析HTML文件的片段。我个人认为,由于浏览器的强大的容错能力,即使一个片段的HTML文件,也可以正确显示,由此也变相地造成了很多人不再关心的HTML的完整要求了。这个类,也许将是用的最多的。下面,看看nutch是如何使用nekoHTML的。

package net.nutch.fetcher;   
...   
import org.cyberneko.html.parsers.*;   
import org.xml.sax.*;   
import org.w3c.dom.*;   
import org.w3c.dom.html.*;   
import org.apache.html.dom.*;   
/* A simple fetcher. */  
public class Fetcher {   
....   
   private DOMFragmentParser parser = new DOMFragmentParser();   
....   
   private void handleFetch(URL url, FetchListEntry fle, Http.Response response)   
     throws IOException, SAXException {   
       
     //判断HTTP应答包的类型,只放过html文件   
     String contentType = response.getHeader("Content-Type");   
     if (contentType != null && !contentType.startsWith("text/html"))   
       throw new IOException("Unknown content-type: " + contentType);   
     //创建文件片段对象   
     DocumentFragment node = new HTMLDocumentImpl().createDocumentFragment();   
     //解析HTML内容   
     parser.parse(new InputSource(new ByteArrayInputStream(response.getContent())),node);   
     //取得全部文本内容   
     StringBuffer sb = new StringBuffer();   
     getText(sb, node);   
     String text = sb.toString();   
     //取得标题信息   
     sb.setLength(0);   
     getTitle(sb, node);   
     String title = sb.toString().trim();   
     //取得该页所有的出链   
     ArrayList l = new ArrayList();   
     getOutlinks(url, l, node);   
       
     //显示结果,存储信息   
     Outlink[] outlinks = (Outlink[])l.toArray(new Outlink[l.size()]);   
     LOG.fine("found " + outlinks.length + " outlinks in " + url);   
     outputPage(new FetcherOutput(fle, MD5Hash.digest(response.getContent()),   
                                  true, title, outlinks),   
                new FetcherContent(response.getContent()),   
                new FetcherText(text));   
   }   
  private static void getText(StringBuffer sb, Node node) {   
   if (node.getNodeType() == Node.TEXT_NODE) {   
     sb.append(node.getNodeValue());//取得结点值,即开始与结束标签之间的信息   
   }   
   NodeList children = node.getChildNodes();   
   if ( children != null ) {   
     int len = children.getLength();   
     for ( int i = 0; i < len; i++ ) {   
       getText(sb, children.item(i));//递归遍历DOM树   
     }   
   }   
  }   
  private static boolean getTitle(StringBuffer sb, Node node) {   
   if (node.getNodeType() == Node.ELEMENT_NODE) {   
     if ("title".equalsIgnoreCase(node.getNodeName())) {   
       getText(sb, node);   
       return true;   
     }   
   }   
   NodeList children = node.getChildNodes();   
   if (children != null) {   
     int len = children.getLength();   
     for (int i = 0; i < len; i++) {   
       if (getTitle(sb, children.item(i))) {   
         return true;   
       }   
     }   
   }   
   return false;   
  }   
  private static void getOutlinks(URL base, ArrayList outlinks, Node node) {   
   if (node.getNodeType() == Node.ELEMENT_NODE) {   
     if ("a".equalsIgnoreCase(node.getNodeName())) {   
       StringBuffer linkText = new StringBuffer();   
       getText(linkText, node);   
       NamedNodeMap attrs = node.getAttributes();   
       String target= null;   
       for (int i= 0; i < attrs.getLength(); i++ ) {   
         if ("href".equalsIgnoreCase(attrs.item(i).getNodeName())) {   
           target= attrs.item(i).getNodeValue();//在DOM树中,属性是一个结点。   
           break;   
         }   
       }   
       if (target != null)   
         try {   
           URL url = new URL(base, target);   
           outlinks.add(new Outlink(url.toString(),linkText.toString().trim()));   
         } catch (MalformedURLException e) {   
           // don't care   
         }   
     }   
   }   
   NodeList children = node.getChildNodes();   
   if ( children != null ) {   
     int len = children.getLength();   
     for ( int i = 0; i &lt; len; i++ ) {   
       getOutlinks(base, outlinks, children.item(i));//递归遍历DOM树   
     }   
   }   
  }   
  ....   
}   
  
//注意,此处传递给解析过程parse的文档片段对象,必须是由//org.w3c.dom.html.HTMLDocument类型的DOM文档对象创建,否则有异常。   
//  HTMLConfiguration可以用于创建任何基于XNI解析器,可参考下例   
package sample;   
import org.apache.xerces.parsers.AbstractSAXParser;   
import org.cyberneko.html.HTMLConfiguration;   
public class HTMLSAXParser extends AbstractSAXParser {   
   public HTMLSAXParser() {   
       super(new HTMLConfiguration());   
   }   
}  


三、设置解析器参数
为了更加精确的控制解析的动作,nekohtml提供了相应的设置函数。如下列:
// settings on HTMLConfiguration
org.apache.xerces.xni.parser.XMLParserConfiguration config =
new org.cyberneko.html.HTMLConfiguration();
config.setFeature("http://cyberneko.org/html/features/augmentations", true);
config.setProperty("http://cyberneko.org/html/properties/names/elems", "lower");

// settings on DOMParser
org.cyberneko.html.parsers.DOMParser parser =
new org.cyberneko.html.parsers.DOMParser();
parser.setFeature("http://cyberneko.org/html/features/augmentations", true);
parser.setProperty("http://cyberneko.org/html/properties/names/elems", "lower");

nekohtml功能(feature)列表
功能 默认值 描述
http://cyberneko.org/html/features/balance-tags True 是否允许增补缺失的标签。如果要以XML方式操作HTML文件,此值必须为真。此处提供设置功能,为了性能的原因。
http://cyberneko.org/html/features/balance-tags/ignore-outside-content False 是否忽略文档根元素以后的数据。如果为false,<html>和<bod>被忽略,所有的内容都被解析。
http://cyberneko.org/html/features/document-fragment False 解析HTML片段时是否作标签增补。此功能不要用在DOMParser上,而要用在DOMFragmentParser上。
http://apache.org/xml/features/scanner/notify-char-refs False 当遇到字符实体引用(如&#x20;)是否将(#x20)报告给相应地文档处理器。
http://apache.org/xml/features/scanner/notify-builtin-refs False 当遇到XML内建的字符实体引用(如&amp;)是否将(amp)报告给相应地文档处理器。
http://cyberneko.org/html/features/scanner/notify-builtin-refs False 当遇到HTML内建的字符实体引用(如&copy;)是否将(copy)报告给相应地文档处理器。
http://cyberneko.org/html/features/scanner/script/strip-comment-delims False 是否剥掉<script>元素中的等注释符。
http://cyberneko.org/html/features/augmentations False 是否将与HTML事件有关的infoset项包括在解析管道中。
http://cyberneko.org/html/features/report-errors False 是否报告错误。

nekohtml属性列表
属性 默认值 值域 描述
http://cyberneko.org/html/properties/filters null XMLDocumentFilter[] 在解析管道的最后按数组顺序追加自定义的处理组件(过滤器),必须为数组类型。
http://cyberneko.org/html/properties/default-encoding Windows-1252 IANA encoding names 默认的HTML文件编码
http://cyberneko.org/html/properties/names/elems upper upper,lower,match 如果整理识别出的元素名称
http://cyberneko.org/html/properties/names/attrs lower upper,lower,no-change 如果整理识别出的属性名称

四、管道过滤器

Xerces Native Interface (XNI)定义了一个解析器配置框架,在那儿一个个解析器以模块化组件的形式组成一个管道。这样一来,通过重新安排已有组件和/或新定制开发的组件,就可完成一个新的解析器配置工作。由于nekohtml是采用这个配置框架开发的,所以对解析器新增功能就很简单通过在默认的nekohtml解析管道的末端增加文档过滤器来实现。
要新开发一个过滤器,很简单地实现xerces2的org.apache.xerces.xni.parser包中的 XMLDocumentFilter接口即可。这个接口,一方面使组件成为管道中上一级的事件处理器,另一方面又成为下级的信息源。针对nekohtml 的过滤器开发,只需简单地扩展org.cyberneko.html.filters包中的DefaultFilter类即可。
将自行开发的过滤器加入管道,可参考以下两种办法:
XMLDocumentFilter noop = new DefaultFilter();
XMLDocumentFilter[] filters = { noop };

XMLParserConfiguration parser = new HTMLConfiguration();
parser.setProperty("http://cyberneko.org/html/properties/filters", filters);
nekohtml的org.cyberneko.html.filters 包中有DefaultFilter、
ElementRemover、Identity、Writer,能实现动态插入内容、删除元素、序列化HTML文档等,不详细述。




htmlparser解析自定义标签功能

import org.htmlparser.tags.CompositeTag;    
    
public class StrongTag extends CompositeTag {    
    
    private static final String mIds[] = {    
        "strong"    
    };    
    private static final String mEndTagEnders[] = {    
        "strong"    
    };    
    
    public Strong()    
    {    
    }    
    
    public String[] getIds()    
    {    
        return mIds;    
    }    
    public String[] getEndTagEnders()    
    {    
        return mEndTagEnders;    
    }    
    
}   


上面就是定义这个tag标签继承CompositeTag
然后最重要的是,在使用的时候要注册这个tag

parser = new Parser(url);
PrototypicalNodeFactory p=new PrototypicalNodeFactory();
p.registerTag(new Li());
parser.setNodeFactory(p);
4行代码注册完成,,一个简单的标签自定义完成。



htmlparser获取网页上所有有用链接的方法


public static void getAllLink(String html, String parentUrl) {   
    Parser parser = new Parser();   
    try {   
        parser.setInputHTML(html);   
        NodeFilter filter = new NodeClassFilter(LinkTag.class);   
        NodeList nodes = parser.parse(filter);   
        for (Node node : nodes.toNodeArray()) {   
            LinkTag linkTag = (LinkTag) node;   
            String link = linkTag.getLink().trim();   
            // 过滤,过滤方法可以添加,比如在增加只爬去本域名或本主机名下的网站等等   
            if (!"".equals(link)) {   
                //处理一下那些不是以“http://”开头的url,比如以"/html/....或 html/...."开头的   
                URI uri = new URI(parentUrl);   
                URI _uri = new URI(uri, link);   
                String newUrl = _uri.toString();   
                urls.add(link);   
            }   
        }   
  
    } catch (ParserException e) {   
        throw new RuntimeException("htmlparser解析html文件时异常" + e);   
    } catch (URIException e) {   
        e.printStackTrace();   
    }   
}  




Java 正则表达

\ 反斜杠
\t 间隔 ('\u0009')
\n 换行 ('\u000A')
\r 回车 ('\u000D')
\d 数字 等价于[0-9]
\D 非数字 等价于[^0-9]
\s 空白符号 [\t\n\x0B\f\r]
\S 非空白符号 [^\t\n\x0B\f\r]
\w 单独字符 [a-zA-Z_0-9]
\W 非单独字符 [^a-zA-Z_0-9]
\f 换页符
\e Escape
\b 一个单词的边界
\B 一个非单词的边界
\G 前一个匹配的结束

^为限制开头
^java     条件限制为以Java为开头字符
$为限制结尾
java$     条件限制为以java为结尾字符
.  条件限制除\n以外任意一个单独字符
java..     条件限制为java后除换行外任意两个字符

两者取一「|」
J|A     J或A
Java|Hello     Java或Hello

限制为连续出现指定次数字符「{a}」
J{2}     JJ
J{3}     JJJ

◆文字替换(首次出现字符)
Pattern pattern = Pattern.compile("正则表达式"); 
Matcher matcher = pattern.matcher("正则表达式 Hello World,正则表达式 Hello World"); 
//替换第一个符合正则的数据 
System.out.println(matcher.replaceFirst("Java")); 


◆去除html标记
Pattern pattern = Pattern.compile("<.+?>", Pattern.DOTALL); 
Matcher matcher = pattern.matcher("<a href=\"index.html\">主页</a>"); 
String string = matcher.replaceAll(""); 
System.out.println(string); 




HtmlCleanner结合xpath用法
 HtmlCleaner cleaner = new HtmlCleaner();      
         
      TagNode node = cleaner.clean(new URL("http://finance.sina.com.cn/money/nmetal/20091209/10157077895.shtml"));      
      //按tag取.      
      Object[] ns = node.getElementsByName("title", true);    //标题      
         
      if(ns.length > 0) {      
          System.out.println("title="+((TagNode)ns[0]).getText());      
      }      
      // /html/body/div[2]/div[4]/div/div/div/div[2]/p   
      ns = node.evaluateXPath("//div[@class=\"blkContainerSblkCon\"]/p"); //选取class为指定blkContainerSblkCon的div下面的所有p标签   
      for (int i = 0; i < ns.length; i++) {   
         String in = cleaner.getInnerHtml((TagNode)ns[i]);   
           System.out.println("<p>"+in + "</p>");   
}   
      String in = cleaner.getInnerHtml((TagNode)ns[0]);   
      System.out.println(in);   
  
      System.out.println(((TagNode)ns[0]).getText());   


 HtmlCleaner cleaner = new HtmlCleaner();      
        String url = "http://finance.sina.com.cn/nmetal/hjfx.html";   
        URL _url = new URL(url);   
        TagNode node = cleaner.clean(_url);      
           
        //按tag取.      
        Object[] ns = node.getElementsByName("title", true);    //标题      
           
        if(ns.length > 0) {    
            System.out.println("title="+((TagNode)ns[0]).getText());      
        }     
           
           
        ns = node.evaluateXPath("//*[@class='Frame-Row3-01-C']/table[2]/tbody/tr/td/a"); //选取class为指定blkContainerSblkCon的div下面的所有p   
        for (int i = 0; i < ns.length; i++) {   
               
            //取链接文本   
//           String in = cleaner.getInnerHtml((TagNode)ns[i]);   
//           System.out.println(in);   
               
            //获取链接的   
            TagNode n = (TagNode) ns[i];   
//          System.out.println(n.getAttributeByName("href"));   
            System.out.println(new URL(_url,n.getAttributeByName("href")).toString());   
        }   
//        String in = cleaner.getInnerHtml((TagNode)ns[0]);   
//        System.out.println(in);   
  
//        System.out.println(((TagNode)ns[0]).getText());   
           
//        System.out.println("ul/li:");      
//        //按xpath取      
//        ns = node.evaluateXPath("//div[@class='d_1']//li");      
//        for(Object on : ns) {      
//            TagNode n = (TagNode) on;      
//            System.out.println("\ttext="+n.getText());      
//        }      
//        System.out.println("a:");      
//        //按属性值取      
//        ns = node.getElementsByAttValue("name", "my_href", true, true);      
//        for(Object on : ns) {      
//            TagNode n = (TagNode) on;      
//            System.out.println("\thref="+n.getAttributeByName("href")+", text="+n.getText());      
//        }      


XPath文档:http://www.w3school.com.cn/xpath/xpath_syntax.asp
  相关解决方案