`
jilong-liang
  • 浏览: 471724 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类

(转)二、基于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.返回子链接集合

OK,上代码:

 

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;
    }
}

 此时可能有读者在想:呵~呵~博主忽略了相对url链接的问题了(-.-)

其实我想到了,一开始我写了一个private方法专门把任何url转换成绝对url链接。后来调试的时候我发现我的方法根本没用,因为htmlparser很人性化地自动完成了这个转换!

另外,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>标签中往往有对网页内容最精炼的描述,开发者应该考虑性能与代价

 

来自:http://www.cnblogs.com/coding-hundredOfYears/archive/2012/12/15/2819217.html

 

分享到:
评论

相关推荐

    Java正则表达式详解+基于HTMLParser解析HTML网页

    如何在Java程序中利用正则表达式实现对字符串的解析.另外,HTMLParser是一款很强大的对HTML网页进行解析的工具,其中大量地用到正则表达式.

    基于HttpClient与HTMLParser 的网页正文提取

    本文研究了 HttpClient、HTMLParser 等技术,提出并实现了一种基于HttpClient 与HTMLParser 的网 页抓取解析方法,该方法能够快速有效对HTML 页面进行抓取解析

    Java编写多个爬虫实例

    HtmlParser 网页内容提取库HtmlParser的源码项目 nekohtml-1.9.7 nekohtml的源码项目 RhinoTest 测试js解析 ExtractContext 内容抽取示例 SearchChinesePdf 包含对PDF文件的处理 parse-rtf 包含对RTF文件的处理 ...

    专用Web信息收集系统的设计和实现

    Heritrix是由Java开发的开源Web网络爬虫,HTMLParser技术对抓取后网页内容进行高效率解析,对信息进行再一次整合,很好的解决了专业搜索引擎所需数据来源问题。文章探讨了基于Heritrix和HTMLParser构建Web信息收集系统...

    基于Java和Python的爬虫项目实战源码.zip

    自己动手写网络爬虫》,并基于Python3和Java实现 为什么采用宽度优先搜索策略? 深度优先遍历可能会在深度上过“深”而陷入“黑洞”; 重要的网页往往距离种子网页比较近,越深的网页的重要性越低; 万维网深度最多...

    Heritrix lucene开发自己的搜索引擎(源码)1

    使用HTMLParser解析网页文件 安装:直接在Eclipse中选取“import-&gt;Existing Project” Eclipse工程/heritrixProject:原书第十章的工程文件 在Eclipse配置完成的Heritrix源代码 自行开发的Heritrix的Extractor类...

    开发自己的搜索引擎lucene and heritrix

    使用HTMLParser解析网页文件 安装:直接在Eclipse中选取“import-&gt;Existing Project” Eclipse工程/heritrixProject:原书第十章的工程文件 在Eclipse配置完成的Heritrix源代码 自行开发的Heritrix的Extractor类...

    Heritrix lucene开发自己的搜索引擎(源码)3

    使用HTMLParser解析网页文件 安装:直接在Eclipse中选取“import-&gt;Existing Project” Eclipse工程/heritrixProject:原书第十章的工程文件 在Eclipse配置完成的Heritrix源代码 自行开发的Heritrix的Extractor类...

    HttpClient以及获取页面内容应用

    通过一个URLConnection或者一个保存有网页内容的字符串来初始化Parser,或者使用静态函数来生成一个Parser对象。 ParserFeedback的代码很简单,是针对调试和跟踪分析过程的,一般不需要改变。而使用Lexer???? ...

    基于python实现的百度音乐下载器python pyqt改进版(附代码)

    前言 之前写过一个用python实现的百度新歌榜、热歌榜下载器的文章,实现了百度新歌、热门歌曲的爬取与下载。但那个采用的是单线程,网络状况一般的情况下,扫描前...3.解析网页的方法由之前的HtmlParser变成了现在的Bea

    java开源包3

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包4

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包1

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包11

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包2

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包6

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包5

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包10

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包8

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    java开源包7

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

Global site tag (gtag.js) - Google Analytics