基于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,上代码:

 1 package Crawler;
2
3
4 import java.util.HashSet;
5 import java.util.Set;
6
7 import org.htmlparser.Node;
8 import org.htmlparser.NodeFilter;
9 import org.htmlparser.Parser;
10 import org.htmlparser.filters.NodeClassFilter;
11 import org.htmlparser.filters.OrFilter;
12 import org.htmlparser.tags.LinkTag;
13 import org.htmlparser.util.NodeList;
14 import org.htmlparser.util.ParserException;
15
16 public class HtmlLinkParser {
17 //获取子链接,url为网页url,filter是链接过滤器,返回该页面子链接的HashSet
18 public static Set<String> extracLinks(String url, LinkFilter filter) {
19
20 Set<String> links = new HashSet<String>();
21 try {
22 Parser parser = new Parser(url);
23 parser.setEncoding("utf-8");
24 // 过滤 <frame >标签的 filter,用来提取 frame 标签里的 src 属性所表示的链接
25 NodeFilter frameFilter = new NodeFilter() {
26 public boolean accept(Node node) {
27 if (node.getText().startsWith("frame src=")) {
28 return true;
29 } else {
30 return false;
31 }
32 }
33 };
34 // OrFilter 接受<a>标签或<frame>标签,注意NodeClassFilter()可用来过滤一类标签,linkTag对应<标签>
35 OrFilter linkFilter = new OrFilter(new NodeClassFilter(
36 LinkTag.class), frameFilter);
37 // 得到所有经过过滤的标签,结果为NodeList
38 NodeList list = parser.extractAllNodesThatMatch(linkFilter);
39 for (int i = 0; i < list.size(); i++) {
40 Node tag = list.elementAt(i);
41 if (tag instanceof LinkTag)// <a> 标签
42 {
43 LinkTag link = (LinkTag) tag;
44 String linkUrl = link.getLink();// 调用getLink()方法得到<a>标签中的链接
45 if (filter.accept(linkUrl))//将符合filter过滤条件的链接加入链接表
46 links.add(linkUrl);
47 } else{// <frame> 标签
48 // 提取 frame 里 src 属性的链接如 <frame src="test.html"/>
49 String frame = tag.getText();
50 int start = frame.indexOf("src=");
51 frame = frame.substring(start);
52 int end = frame.indexOf(" ");
53 if (end == -1)
54 end = frame.indexOf(">");
55 String frameUrl = frame.substring(5, end - 1);
56 if (filter.accept(frameUrl))
57 links.add(frameUrl);
58 }
59 }
60 } catch (ParserException e) {//捕捉parser的异常
61 e.printStackTrace();
62 }
63 return links;
64 }
65 }

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

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

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

2.解析网页内容:

基本思路:

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

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

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

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

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

  1 import java.io.BufferedReader;
2 import java.io.FileInputStream;
3 import java.io.FileNotFoundException;
4 import java.io.FileReader;
5 import java.io.IOException;
6 import java.io.InputStreamReader;
7 import java.util.regex.Matcher;
8 import java.util.regex.Pattern;
9
10 import org.htmlparser.Parser;
11 import org.htmlparser.filters.NodeClassFilter;
12 import org.htmlparser.tags.HeadingTag;
13 import org.htmlparser.tags.LinkTag;
14 import org.htmlparser.tags.MetaTag;
15 import org.htmlparser.tags.ParagraphTag;
16 import org.htmlparser.tags.TitleTag;
17 import org.htmlparser.util.NodeList;
18 import org.htmlparser.util.ParserException;
19
20 import java.util.Set;
21 import multi.patt.match.ac.*;
22
23 public class HtmlFileParser {
24 String filepath=new String();//html文件路径
25 private static String[] keyWords;//关键词列表
26 /*static{
27 keyWords=read("filePath");//从指定文件中读取关键词列表
28 }*/
29 public HtmlFileParser(String filepath){
30 this.filepath=filepath;
31 }
32 public String getTitle(){//得到页面标题
33 FileAndEnc fae=readHtmlFile();
34 int i=0;
35 try{
36 //实例化一个本地html文件的Parser
37 Parser titleParser = Parser.createParser(fae.getFile(),fae.getEnc());
38 NodeClassFilter titleFilter =new NodeClassFilter(TitleTag.class);
39 NodeList titleList = titleParser.extractAllNodesThatMatch(titleFilter);
40 //实际上一个网页应该只有一个<title>标签,但extractAllNodesThatMatch方法返回的只能是一个NodeList
41 for (i = 0; i < titleList.size(); i++) {
42 TitleTag title_tag = (TitleTag) titleList.elementAt(i);
43 return title_tag.getTitle();
44 }
45 }catch(ParserException e) {
46 return null;
47 }
48 return null;
49 }
50 public String getEncoding(){//获得页面编码
51 FileAndEnc fae=readHtmlFile();
52 return fae.getEnc();
53 }
54 public float getRelatGrade(){//计算网页的主题相关度
55 FileAndEnc fae=readHtmlFile();
56 String file=fae.getFile();
57 String enC=fae.getEnc();
58 String curString;
59 int curWordWei = 1;//当前关键词权重
60 float curTagWei = 0;//当前标签权重
61 float totalGra = 0;//总相关度分
62 int i;
63 AcApply obj = new AcApply();//实例化ac自动机
64 Pattern p = null;
65 Matcher m = null;
66 try{//根据不同标签依次进行相关度计算
67 //title tag <title>
68 curTagWei=5;
69 Parser titleParser = Parser.createParser(file,enC);
70 NodeClassFilter titleFilter =new NodeClassFilter(TitleTag.class);
71 NodeList titleList = titleParser.extractAllNodesThatMatch(titleFilter);
72 for (i = 0; i < titleList.size(); i++) {
73 TitleTag titleTag=(TitleTag)titleList.elementAt(i);
74 curString=titleTag.getTitle();
75 Set result = obj.findWordsInArray(keyWords, curString);//ac自动机的方法返回匹配的词的表
76 totalGra=totalGra+result.size()*curTagWei;//计算相关度
77 }
78 //meta tag of description and keyword <meta>
79 curTagWei=4;
80 Parser metaParser = Parser.createParser(file,enC);
81 NodeClassFilter metaFilter =new NodeClassFilter(MetaTag.class);
82 NodeList metaList = metaParser.extractAllNodesThatMatch(metaFilter);
83 p = Pattern.compile("\\b(description|keywords)\\b",Pattern.CASE_INSENSITIVE);
84 for (i = 0; i < metaList.size(); i++) {
85 MetaTag metaTag=(MetaTag)metaList.elementAt(i);
86 curString=metaTag.getMetaTagName();
87 if(curString==null){
88 continue;
89 }
90 m = p.matcher(curString); //正则匹配name是description或keyword的<meta>标签
91 if(m.find()){
92 curString=metaTag.getMetaContent();//提取其content
93 Set result = obj.findWordsInArray(keyWords, curString);
94 totalGra=totalGra+result.size()*curTagWei;
95 }
96 else{
97 curString=metaTag.getMetaContent();
98 Set result = obj.findWordsInArray(keyWords, curString);
99 totalGra=totalGra+result.size()*2;
100 }
101 }
102 //heading tag <h*>
103 curTagWei=3;
104 Parser headingParser = Parser.createParser(file,enC);
105 NodeClassFilter headingFilter =new NodeClassFilter(HeadingTag.class);
106 NodeList headingList = headingParser.extractAllNodesThatMatch(headingFilter);
107 for (i = 0; i < headingList.size(); i++) {
108 HeadingTag headingTag=(HeadingTag)headingList.elementAt(i);
109 curString=headingTag.toPlainTextString();//得到<h*>标签中的纯文本
110 if(curString==null){
111 continue;
112 }
113 Set result = obj.findWordsInArray(keyWords, curString);
114 totalGra=totalGra+result.size()*curTagWei;
115 }
116 //paragraph tag <p>
117 curTagWei=(float)2.5;
118 Parser paraParser = Parser.createParser(file,enC);
119 NodeClassFilter paraFilter =new NodeClassFilter(ParagraphTag.class);
120 NodeList paraList = paraParser.extractAllNodesThatMatch(paraFilter);
121 for (i = 0; i < paraList.size(); i++) {
122 ParagraphTag paraTag=(ParagraphTag)paraList.elementAt(i);
123 curString=paraTag.toPlainTextString();
124 if(curString==null){
125 continue;
126 }
127 Set result = obj.findWordsInArray(keyWords, curString);
128 totalGra=totalGra+result.size()*curTagWei;
129 }
130 //link tag <a>
131 curTagWei=(float)0.25;
132 Parser linkParser = Parser.createParser(file,enC);
133 NodeClassFilter linkFilter =new NodeClassFilter(LinkTag.class);
134 NodeList linkList = linkParser.extractAllNodesThatMatch(linkFilter);
135 for (i = 0; i < linkList.size(); i++) {
136 LinkTag linkTag=(LinkTag)linkList.elementAt(i);
137 curString=linkTag.toPlainTextString();
138 if(curString==null){
139 continue;
140 }
141 Set result = obj.findWordsInArray(keyWords, curString);
142 totalGra=totalGra+result.size()*curTagWei;
143 }
144 }catch(ParserException e) {
145 return 0;
146 }
147 return totalGra;
148 }
149 private FileAndEnc readHtmlFile(){//读取html文件,返回字符串格式的文件与其编码
150 StringBuffer abstr = new StringBuffer();
151 FileAndEnc fae=new FileAndEnc();
152 try{
153 //实例化默认编码方式的BufferefReader
154 BufferedReader enCReader= new BufferedReader(new InputStreamReader(new FileInputStream(filepath),"UTF-8"));
155 String temp=null;
156 while((temp=enCReader.readLine())!=null){//得到字符串格式的文件
157 abstr.append(temp);
158 abstr.append("\r\n");
159 }
160 String result=abstr.toString();
161 fae.setFile(result);
162 String encoding=getEnc(result);
163 fae.setEnc(encoding);//得到页面编码
164 //根据得到的编码方式实例化BufferedReader
165 BufferedReader reader= new BufferedReader(new InputStreamReader(new FileInputStream(filepath),encoding));
166 StringBuffer abstrT = new StringBuffer();
167 while((temp=reader.readLine())!=null){
168 abstrT.append(temp);
169 abstrT.append("\r\n");
170 }
171 result=abstrT.toString();
172 fae.setFile(result);//得到真正的页面内容
173 } catch (FileNotFoundException e) {
174 System.out.println("file not found");
175 fae=null;
176 } catch (IOException e) {
177 // TODO Auto-generated catch block
178 e.printStackTrace();
179 fae=null;
180 } finally {
181 return fae;
182 }
183 }
184 private String getEnc(String file){//根据正则匹配得到页面编码
185 String enC="utf-8";
186 Pattern p = Pattern.compile("(charset|Charset|CHARSET)\\s*=\\s*\"?\\s*([-\\w]*?)[^-\\w]");
187 Matcher m = p.matcher(file);
188 if(m.find()){
189 enC=m.group(2);
190 }
191 return enC;
192 }
193 }

读者需要注意两点:

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

好,我的经验就介绍完了。我还很菜,如有说的不对、讲得不好的地方望读者指正、提出建议!

 
 

基于htmlparser实现网页内容解析的更多相关文章

  1. 史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南-C++

    目录 史上最最靠谱,又双叒叒简单的基于MSXML的XML解析指南 流程设计 xml信息有哪几种读取形式(xml文件或wchar) 如何选取节点,and取节点属性有哪些方法? IXMLDOMNode与I ...

  2. 基于Jquery的XML解析器,返回定制的HTML

    依据HTML模板返回解析的XML 依赖jQuery 1.4​1. [代码]基于Jquery的xml解析器并返回定制的HTML     /** *  jQuery插件 *  Author: pureco ...

  3. OO第四单元——基于UML的UML解析器总结&OO课程总结

    OO第四单元--基于UML的UML解析器总结&OO课程总结 前言:一学期愉快(痛苦)的OO课程学习结束了,OO几个单元作业都各有特色,实验也各有特色,仔细回味起来,不再是单纯的敲代码(但自己还 ...

  4. OSS.Core基于Dapper封装(表达式解析+Emit)仓储层的构思及实现

    最近趁着不忙,在构思一个搭建一个开源的完整项目,至于原因以及整个项目框架后边文章我再说明.既然要起一个完整的项目,那么数据仓储访问就必不可少,这篇文章我主要介绍这个新项目(OSS.Core)中我对仓储 ...

  5. Python:使用基于事件驱动的SAX解析XML

    SAX的特点: 是基于事件的 API 在一个比 DOM 低的级别上操作 为您提供比 DOM 更多的控制 几乎总是比 DOM 更有效率 但不幸的是,需要比 DOM 更多的工作 基于对象和基于事件的接口 ...

  6. Java HttpURLConnection 抓取网页内容 解析gzip格式输入流数据并转换为String格式字符串

    最近GFW为了刷存在感,搞得大家是头晕眼花,修改hosts 几乎成了每日必备工作. 索性写了一个小程序,给办公室的同事们分享,其中有个内容 就是抓取网络上的hosts,废了一些周折. 我是在一个博客上 ...

  7. NullSafe基于Runtime的深度解析

    Objective-C是一门动态语言,一个函数是由一个selector(SEL),和一个implement(IML)组成的. 执行一个方法时如果系统找不到方法会给几次机会寻找方法,实在没有此方法就会抛 ...

  8. 几种基于Java的SQL解析工具的比较与调用

    1.sqlparser http://www.sqlparser.com/ 优点:支持的数据库最多,除了传统数据库外还支持hive和greenplum一类比较新的数据库,调用比较方便,功能不错 缺点: ...

  9. Python 爬虫 —— 网页内容解析(lxml)

    0. xpath 语法 找到所有 <img src=....> 图像的链接: xpath = './/img/@src' img_urls = html.xpath(xpath) @修饰节 ...

随机推荐

  1. DOCKER脚本一例---快速建立大批测试机

    这个会由一系列的脚本构成,比如: 系统重启后,如何快速恢复服务,如何建立网桥(也可一次写入),如何在新系统上快速部署. ADDBRIDGE #!/bin/sh br_name=br100 brctl ...

  2. Delphi 自带的 Base64 编解码函数

    今天帮别人解决一个关于 Base64 编解码的问题,竟然发现 Delphi 自带了 Base64 编解码的单元,叫 EncdDecd,这名字很拗口而且不直观,估计这是一直很少人关注和知道的原因. 这个 ...

  3. 迅雷程浩:企业外包服务,下一个大的风口?(2B业务一定要懂销售和营销的人,这点和2C 不一样)

    我今年暑假去了趟硅谷,一天去一个朋友的公司拜访,发现这公司没有前台,前台桌子上放了一个显示器.我刚进去,显示器里的老印就跟我打招呼 "How may I help you?" 事后 ...

  4. 2015第27周三Java内存模型

    自己写的代码,6个月不看也是别人的代码,自己学的知识也同样如此,学完的知识如果不使用或者不常常回顾,那么还不是自己的知识. 要认识java线程安全,必须了解两个主要的点:java的内存模型,java的 ...

  5. [面试题总结及扩展知识]HTTP协议返回状态码的问题

    经常在网页中看到一些错误的返回信息,见一个查一个已经累感不爱,在2014年腾讯笔试题中也见到一道这样的问题,所以现在来总结一下: 腾讯2014面试题: 答案选B 附带一些http协议的错误代码: 当服 ...

  6. jsp中pageEncoding、charset=UTF -8

    jsp中pageEncoding.charset=UTF -8" 在JSP/Servlet  中主要有以下几个地方可以设置编码,pageEncoding="UTF-8". ...

  7. Servlet的init()方法如何才会在服务器启动时执行

    如果要想让 servlet 的 init () 方法在服务器启动 时就被执行,则需要在 web.xml 中相应的 servlet 下配置 <servlet > <servlet -n ...

  8. html(三)

    今天自己画了个安卓机器人,之前听徐大大讲过一次,查手册去动手的时候其实发觉不是很难,这种规则的图像还是很好画的,主要是用<div>标签和<span>标签去做的,通过CSS添加样 ...

  9. POJ 1637 混合图求欧拉回路 最大流实现

    前面讲过了无向图,有向图求欧拉回路,欧拉通路的做法.可以直接根据度数来判断,当然前提是这是一个连通图. 这道题既有无向边,又有有向边,然后求欧拉回路. 采用的方法是最大流. 具体处理方法. 首先,我们 ...

  10. Swift补基础之Selector、条件编译、编译标记、NSObject

    在swift中使用条件编译比较直接 #if <condition> #elseif <condition> #else #endif 例如 :在debug模式和release模 ...