基于htmlparser实现网页内容解析

网页解析,即程序自动分析网页内容、获取信息,从而进一步处理信息。

网页解析是实现网络爬虫中不可缺少而且十分重要的一环.。

首先,必须说在最前的是我们使用的工具——htmlparser

简要地说,htmlparser包提供方便、简洁的处理html文件的方法,它将html页面中的标签按树形结构解析成一个一个结点,一种类型的结点对应一个类,通过调用其方法可以轻松地访问标签中的内容。

我所使用的是htmlparser2.0,也就是最新版本。强烈推荐。


对于主题爬虫,它的功能就是将与主题相关的网页下载到本地,将网页的相关信息存入数据库。

网页解析模块要实现两大功能:1.从页面中提取出子链接,加入到爬取url队列中;2.解析网页内容,与主题进行相关度计算。

由于网页内容解析需要频繁地访问网页文件,如果通过url访问网络获取文件的时间开销比较大,所以我们的做法是将爬取队列中的网页统统下载到本地,对本地的网页文件进行页面内容解析,最后删除不匹配的网页。而子链接的提取比较简单,通过网络获取页面文件即可。对于给定url通过网络访问网页,和给定文件路径访问本地网页文件,htmlparser都是支持的

1.子链接的提取:

做页面子链接提取的基本思路是:

1.用被提取的网页的url实例化一个Parser

2.实例化Filter,设置页面过滤条件——只获取<a>标签与<frame>标签的内容

3.用Parser提取页面中所有通过Filter的结点,得到NodeList

4.遍历NodeList,调用Node的相应方法得到其中的链接,加入子链接的集合

5.返回子链接集合



 package Crawler;
  
  
 import java.util.HashSet;
 import java.util.Set;
 
 import org.htmlparser.Node;
 import org.htmlparser.NodeFilter;
 import org.htmlparser.Parser;
 import org.htmlparser.filters.NodeClassFilter;
 import org.htmlparser.filters.OrFilter;
 import org.htmlparser.tags.LinkTag;
 import org.htmlparser.util.NodeList;
 import org.htmlparser.util.ParserException;
 
 public class HtmlLinkParser {
     //获取子链接,url为网页url,filter是链接过滤器,返回该页面子链接的HashSet
     public static Set<String> extracLinks(String url, LinkFilter filter) {
 
         Set<String> links = new HashSet<String>();
         try {
             Parser parser = new Parser(url);
             parser.setEncoding("utf-8");
             // 过滤 <frame >标签的 filter,用来提取 frame 标签里的 src 属性所表示的链接
             NodeFilter frameFilter = new NodeFilter() {
                 public boolean accept(Node node) {
                     if (node.getText().startsWith("frame src=")) {
                         return true;
                     } else {
                         return false;
                     }
                 }
             };
             // OrFilter 接受<a>标签或<frame>标签,注意NodeClassFilter()可用来过滤一类标签,linkTag对应<标签>
             OrFilter linkFilter = new OrFilter(new NodeClassFilter(
                     LinkTag.class), frameFilter);
             // 得到所有经过过滤的标签,结果为NodeList
             NodeList list = parser.extractAllNodesThatMatch(linkFilter);
             for (int i = 0; i < list.size(); i++) {
                 Node tag = list.elementAt(i);
                 if (tag instanceof LinkTag)// <a> 标签
                 {
                     LinkTag link = (LinkTag) tag;
                     String linkUrl = link.getLink();// 调用getLink()方法得到<a>标签中的链接
                     if (filter.accept(linkUrl))//将符合filter过滤条件的链接加入链接表
                         links.add(linkUrl);
                 } else{// <frame> 标签
                     // 提取 frame 里 src 属性的链接如 <frame src="test.html"/>
                     String frame = tag.getText();
                     int start = frame.indexOf("src=");
                     frame = frame.substring(start);
                     int end = frame.indexOf(" ");
                     if (end == -1)
                         end = frame.indexOf(">");
                     String frameUrl = frame.substring(5, end - 1);
                     if (filter.accept(frameUrl))
                         links.add(frameUrl);
                 }
             }
         } catch (ParserException e) {//捕捉parser的异常
             e.printStackTrace();
         }
         return links;
     }
 }



Parser是需要设置编码的,在这段程序中我直接设置为utf-8。实际上网页的编码方式是多种多样的,在<meta>标签中有关于编码方式的信息,如果编码不正确,页面的文本内容可能是乱码。不过,在子链接提取的部分,我们仅对标签内部的内容进行处理,这些内容是根据html语法编写的,不涉及编码的问题。

2.解析网页内容:

基本思路:

1.读取html文件,获得页面编码,获得String格式的文件内容

2.用页面编码实例化html文件的Parser

3.对需要提取的结点设置相应的Filter

4.根据给定的Filter,用Parser解析html文件

5.提取结点中的文本内容,进行处理(本例中是关键字匹配,计算主题相关度)


  import java.io.BufferedReader;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.FileReader;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import org.htmlparser.Parser;
  import org.htmlparser.filters.NodeClassFilter;
  import org.htmlparser.tags.HeadingTag;
  import org.htmlparser.tags.LinkTag;
  import org.htmlparser.tags.MetaTag;
  import org.htmlparser.tags.ParagraphTag;
  import org.htmlparser.tags.TitleTag;
  import org.htmlparser.util.NodeList;
  import org.htmlparser.util.ParserException;
  
  import java.util.Set;
  import multi.patt.match.ac.*;
  
  public class HtmlFileParser {
      String filepath=new String();//html文件路径
      private static String[] keyWords;//关键词列表
      /*static{
          keyWords=read("filePath");//从指定文件中读取关键词列表
      }*/
      public HtmlFileParser(String filepath){
          this.filepath=filepath;
      }
      public String getTitle(){//得到页面标题
          FileAndEnc fae=readHtmlFile();
          int i=0;
          try{
              //实例化一个本地html文件的Parser
              Parser titleParser = Parser.createParser(fae.getFile(),fae.getEnc());
              NodeClassFilter titleFilter =new NodeClassFilter(TitleTag.class);
              NodeList titleList = titleParser.extractAllNodesThatMatch(titleFilter);
              //实际上一个网页应该只有一个<title>标签,但extractAllNodesThatMatch方法返回的只能是一个NodeList
              for (i = 0; i < titleList.size(); i++) {
                  TitleTag title_tag = (TitleTag) titleList.elementAt(i);
                  return title_tag.getTitle();
              }
          }catch(ParserException e) {
              return null;
          }
          return null;
      }
      public String getEncoding(){//获得页面编码
          FileAndEnc fae=readHtmlFile();
          return fae.getEnc();
      }
      public float getRelatGrade(){//计算网页的主题相关度
          FileAndEnc fae=readHtmlFile();
          String file=fae.getFile();
          String enC=fae.getEnc();
          String curString;
          int curWordWei = 1;//当前关键词权重
          float curTagWei = 0;//当前标签权重
          float totalGra = 0;//总相关度分
          int i;
          AcApply obj = new AcApply();//实例化ac自动机
          Pattern p = null;
          Matcher m = null;
          try{//根据不同标签依次进行相关度计算
              //title tag    <title>
              curTagWei=5;
              Parser titleParser = Parser.createParser(file,enC);
              NodeClassFilter titleFilter =new NodeClassFilter(TitleTag.class);
              NodeList titleList = titleParser.extractAllNodesThatMatch(titleFilter);
              for (i = 0; i < titleList.size(); i++) {
                  TitleTag titleTag=(TitleTag)titleList.elementAt(i);
                  curString=titleTag.getTitle();
                  Set result = obj.findWordsInArray(keyWords, curString);//ac自动机的方法返回匹配的词的表
                  totalGra=totalGra+result.size()*curTagWei;//计算相关度
              }
              //meta tag of description and keyword <meta>
              curTagWei=4;
              Parser metaParser = Parser.createParser(file,enC);
             NodeClassFilter metaFilter =new NodeClassFilter(MetaTag.class);
              NodeList metaList = metaParser.extractAllNodesThatMatch(metaFilter);
              p = Pattern.compile("\\b(description|keywords)\\b",Pattern.CASE_INSENSITIVE);
              for (i = 0; i < metaList.size(); i++) {
                  MetaTag metaTag=(MetaTag)metaList.elementAt(i);
                  curString=metaTag.getMetaTagName();
                  if(curString==null){
                      continue;
                  }
                  m = p.matcher(curString); //正则匹配name是description或keyword的<meta>标签
                  if(m.find()){
                      curString=metaTag.getMetaContent();//提取其content
                      Set result = obj.findWordsInArray(keyWords, curString);
                      totalGra=totalGra+result.size()*curTagWei;
                  }
                  else{
                      curString=metaTag.getMetaContent();
                      Set result = obj.findWordsInArray(keyWords, curString);
                      totalGra=totalGra+result.size()*2;
                 }
             }
             //heading tag <h*>
             curTagWei=3;
             Parser headingParser = Parser.createParser(file,enC);
             NodeClassFilter headingFilter =new NodeClassFilter(HeadingTag.class);
             NodeList headingList = headingParser.extractAllNodesThatMatch(headingFilter);
             for (i = 0; i < headingList.size(); i++) {
                 HeadingTag headingTag=(HeadingTag)headingList.elementAt(i);
                 curString=headingTag.toPlainTextString();//得到<h*>标签中的纯文本
                 if(curString==null){
                     continue;
                 }
                 Set result = obj.findWordsInArray(keyWords, curString);
                 totalGra=totalGra+result.size()*curTagWei;
             }
             //paragraph tag <p>
             curTagWei=(float)2.5;
             Parser paraParser = Parser.createParser(file,enC);
             NodeClassFilter paraFilter =new NodeClassFilter(ParagraphTag.class);
             NodeList paraList = paraParser.extractAllNodesThatMatch(paraFilter);
             for (i = 0; i < paraList.size(); i++) {
                 ParagraphTag paraTag=(ParagraphTag)paraList.elementAt(i);
                 curString=paraTag.toPlainTextString();
                 if(curString==null){
                     continue;
                 }
                 Set result = obj.findWordsInArray(keyWords, curString);
                 totalGra=totalGra+result.size()*curTagWei;
             }
             //link tag <a>
             curTagWei=(float)0.25;
             Parser linkParser = Parser.createParser(file,enC);
             NodeClassFilter linkFilter =new NodeClassFilter(LinkTag.class);
             NodeList linkList = linkParser.extractAllNodesThatMatch(linkFilter);
             for (i = 0; i < linkList.size(); i++) {
                 LinkTag linkTag=(LinkTag)linkList.elementAt(i);
                 curString=linkTag.toPlainTextString();
                 if(curString==null){
                     continue;
                 }
                 Set result = obj.findWordsInArray(keyWords, curString);
                 totalGra=totalGra+result.size()*curTagWei;
             }        
         }catch(ParserException e) {
             return 0;
         }
         return totalGra;
     }
     private FileAndEnc readHtmlFile(){//读取html文件,返回字符串格式的文件与其编码
         StringBuffer abstr = new StringBuffer();
         FileAndEnc fae=new FileAndEnc();
         try{
             //实例化默认编码方式的BufferefReader
             BufferedReader enCReader= new BufferedReader(new InputStreamReader(new FileInputStream(filepath),"UTF-8"));
             String temp=null;
             while((temp=enCReader.readLine())!=null){//得到字符串格式的文件
                 abstr.append(temp);
                 abstr.append("\r\n");
             }
             String result=abstr.toString();
             fae.setFile(result);
             String encoding=getEnc(result);
             fae.setEnc(encoding);//得到页面编码
             //根据得到的编码方式实例化BufferedReader
             BufferedReader reader= new BufferedReader(new InputStreamReader(new FileInputStream(filepath),encoding));
             StringBuffer abstrT = new StringBuffer();
             while((temp=reader.readLine())!=null){
                 abstrT.append(temp);
                 abstrT.append("\r\n");
             }
             result=abstrT.toString();
             fae.setFile(result);//得到真正的页面内容
         } catch (FileNotFoundException e) {
             System.out.println("file not found");
             fae=null;
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
             fae=null;
         } finally {
             return fae;
         }
     }
     private String getEnc(String file){//根据正则匹配得到页面编码
         String enC="utf-8";
         Pattern p = Pattern.compile("(charset|Charset|CHARSET)\\s*=\\s*\"?\\s*([-\\w]*?)[^-\\w]"); 
         Matcher m = p.matcher(file);
         if(m.find()){ 
             enC=m.group(2);
         }
         return enC;
     }
 }

 

 

 读者需要注意两点:

1.用BufferedReader读取文件是需要编码方式的,但是第一次读取我们必然不知道网页的编码。好在网页对于编码的描述在html语言框架中,我们用默认的编码方式读取文件就可以获取编码。但这个读取的文件的文本内容可能因为编码不正确而产生乱码,所以得到编码后,我们应使用得到的编码再实例化一个BufferedReader读取文件,这样得到的文件就是正确的了(除非网页本身给的编码就不对)。

获得正确的编码对于解析网页内容是非常重要的,而网络上什么样的网页都有,我推荐使用比较基础、可靠的方法获得编码,我使用的是正则匹配。

举个例子:

这是http://kb.cnblogs.com/page/143965/的对编码的描述:

<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>

这是http://www.ucsd.edu/的对编码的描述:

<meta charset="utf-8"/>

2.不熟悉html的读者可能有所不知<meta>的作用,来看看博客园首页的源码:

<meta name="keywords" content="博客园,开发者,程序员,软件开发,编程,代码,极客,Developer,Programmer,Coder,Code,Coding,Greek,IT学习"/><meta name="description" content="博客园是面向程序员的高品质IT技术学习社区,是程序员学习成长的地方。博客园致力于为程序员打造一个优秀的互联网平台,帮助程序员学好IT技术,更好地用技术改变世界。" />

这两类<meta>标签的很好的描述了网页的内容

@编辑 博客园首页这个keyword的内容里这“Greek”……极客是“Geek”,“Greek”是希腊人

3.由于网页的正文通常是一段最长的纯文本内容,所以当我们得到一个<p>,<li>,<ul>标签的纯文本后,我们可以通过判断字符串的长度来得到网页的正文。

对页面大量的信息进行处理是很费时的,页面的<title>标签和<meta>标签中往往有对网页内容最精炼的描述,开发者应该考虑性能与代价

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值