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提供了相应的设置函数。如下列:

//settingsonHTMLConfiguration

org.apache.xerces.xni.parser.XMLParserConfigurationconfig=

neworg.cyberneko.html.HTMLConfiguration();

config.setFeature("http://cyberneko.org/html/features/augmentations",true);

config.setProperty("http://cyberneko.org/html/properties/names/elems","lower");

//settingsonDOMParser

org.cyberneko.html.parsers.DOMParserparser=

neworg.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-tagsTrue是否允许增补缺失的标签。如果要以XML方式操作HTML文件,此值必须为真。此处提供设置功能,为了性能的原因。

http://cyberneko.org/html/features/balance-tags/ignore-outside-contentFalse是否忽略文档根元素以后的数据。如果为false,<html>和<bod>被忽略,所有的内容都被解析。

http://cyberneko.org/html/features/document-fragmentFalse解析HTML片段时是否作标签增补。此功能不要用在DOMParser上,而要用在DOMFragmentParser上。

http://apache.org/xml/features/scanner/notify-char-refsFalse当遇到字符实体引用(如&#x20;)是否将(#x20)报告给相应地文档处理器。

http://apache.org/xml/features/scanner/notify-builtin-refsFalse当遇到XML内建的字符实体引用(如&amp;)是否将(amp)报告给相应地文档处理器。

http://cyberneko.org/html/features/scanner/notify-builtin-refsFalse当遇到HTML内建的字符实体引用(如&copy;)是否将(copy)报告给相应地文档处理器。

http://cyberneko.org/html/features/scanner/script/strip-comment-delimsFalse是否剥掉<script>元素中的等注释符。

http://cyberneko.org/html/features/augmentationsFalse是否将与HTML事件有关的infoset项包括在解析管道中。

http://cyberneko.org/html/features/report-errorsFalse是否报告错误。

nekohtml属性列表

属性默认值值域描述

http://cyberneko.org/html/properties/filtersnullXMLDocumentFilter[]在解析管道的最后按数组顺序追加自定义的处理组件(过滤器),必须为数组类型。

http://cyberneko.org/html/properties/default-encodingWindows-1252IANAencodingnames默认的HTML文件编码

http://cyberneko.org/html/properties/names/elemsupperupper,lower,match如果整理识别出的元素名称

http://cyberneko.org/html/properties/names/attrslowerupper,lower,no-change如果整理识别出的属性名称

四、管道过滤器

XercesNativeInterface(XNI)定义了一个解析器配置框架,在那儿一个个解析器以模块化组件的形式组成一个管道。这样一来,通过重新安排已有组件和/或新定制开发的组件,就可完成一个新的解析器配置工作。由于nekohtml是采用这个配置框架开发的,所以对解析器新增功能就很简单通过在默认的nekohtml解析管道的末端增加文档过滤器来实现。

要新开发一个过滤器,很简单地实现xerces2的org.apache.xerces.xni.parser包中的XMLDocumentFilter接口即可。这个接口,一方面使组件成为管道中上一级的事件处理器,另一方面又成为下级的信息源。针对nekohtml的过滤器开发,只需简单地扩展org.cyberneko.html.filters包中的DefaultFilter类即可。

将自行开发的过滤器加入管道,可参考以下两种办法:

XMLDocumentFilternoop=newDefaultFilter();

XMLDocumentFilter[]filters={noop};

XMLParserConfigurationparser=newHTMLConfiguration();

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=newParser(url);

PrototypicalNodeFactoryp=newPrototypicalNodeFactory();

p.registerTag(newLi());

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换页符

\eEscape

\b一个单词的边界

\B一个非单词的边界

\G前一个匹配的结束

^为限制开头

^java条件限制为以Java为开头字符

$为限制结尾

java$条件限制为以java为结尾字符

.条件限制除\n以外任意一个单独字符

java..条件限制为java后除换行外任意两个字符

两者取一「|」

J|AJ或A

Java|HelloJava或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

相关推荐