當前位置:
首頁 > 知識 > 基於htmlparser實現網頁內容解析

基於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("
");

}

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("
");

}

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>標籤中往往有對網頁內容最精鍊的描述,開發者應該考慮性能與代價

基於htmlparser實現網頁內容解析

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

python 畫餅圖
js中setTimeOut()和setInterval()的使用——程序執行時間控制

TAG:程序員小新人學習 |