您的位置:首页 > Web前端 > HTML

基于htmlparser实现网页内容解析

2016-05-17 14:03 375 查看
网页解析,即程序自动分析网页内容、获取信息,从而进一步处理信息。

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

首先,必须说在最前的是我们使用的工具——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(">"
4000
);
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);
t
db1b
otalGra=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>标签中往往有对网页内容最精炼的描述,开发者应该考虑性能与代价
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: