自己动手写搜索引擎(常搜吧历程七#解析文档之HTML#)(Java、Lucene、hadoop)

一、垂直搜索介绍

1、垂直搜索--即需要抓去大量的网页,分析其中的数据。垂直搜索更着重于正文内容模式分离,数据调整,相关链接分析,是一种结构化分析过程。

2、垂直搜索技术主要分为两个层次:模版级和网页库级

3、模版级是针对网页进行模版设定或者自动生成模版的方式抽取数据,对网页的采集也是针对性的采集,适合规模比较小、信息源少且稳定的需求,优点是快速实施、成本低、灵活性强,缺点是后期维护成本高,信息源和信息量小。

4、网页库级和模版方式最大的区别是对具体网页不依赖,可针对任意正常的网页信息采集信息抽取。缺点是其灵活性差、成本高。


垂直搜索大致需要以下技术:

1、信息采集技术

2、网页信息抽取技术

3、信息的处理技术(重复识别、聚类、比较、分析等)

4、语意相关性分析

5、分词

6、索引


二、使用HttpClient采集页面

1、HTTPClient项目就是专门设计来简化HTTP客户端与服务器进行各种通讯编程。

2、HTTPClient是基于HttpCore实现的一个HTTP/1.1兼容HTTP客户端,它提供了一系列可重用的客户端身份验证、HTTP状态保持、HTTP链接管理module。功能丰富的HTTPClient同时兼具出色的可拓展性和健壮性,目前已经成为了最为流行的JavaHttp客户端组件,为开发Web浏览器、WebService客户端提供了很大的便利。

下载地址: http://hc.apache.org/downloads.cgi

使用HttpClient

一般情况下,使用HttpClient需要以下5个步骤:

1、创建HttpClient的实例

2、创建某种连接方法的实例,在这里是最常见的是Get和Post

3、调用第一步中创建好的HttpClient实例的execute方法,得到执行结果

4、释放连接

5、对得到后的内容进行处理


下面来看两个抓去网页的实例

1、抓取搜索后的页面然后保存

package com.qianyan.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

public class TestHttpClient {

	public static void main(String[] args) throws ClientProtocolException, IOException {
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet("http://www.baidu.com/s?wd=java");
		HttpResponse response = httpClient.execute(httpGet);
		HttpEntity entity = response.getEntity();

		InputStream ins = entity.getContent();
		BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
		FileWriter writer = new FileWriter(new File("E:/baidu.htm"));

		String strLine = reader.readLine();
		while (null != strLine) {
			writer.write(strLine);
			strLine = reader.readLine();
		}
		writer.close();
		ins.close();
		reader.close();
		httpClient.getConnectionManager().shutdown();
		System.out.println("网页生成完毕!");
	}
}

1、抓取搜索后的页面打印出来
package com.qianyan.test;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

public class TestHttpClient2 {

	public static void main(String[] args) {
	    HttpClient httpClient = new DefaultHttpClient();  
	    try {  
	          
	        //创建HttpGet  
	        HttpGet httpGet = new HttpGet("http://www.ifeng.com");  
	        System.err.println("executing request " + httpGet.getURI());  
	        //执行get请求  
	        HttpResponse response = httpClient.execute(httpGet);  
	        //获取响应实体  
	        HttpEntity entity = response.getEntity();  
	        System.err.println("======================================================");  
	        //打印响应状态  
	        System.out.println(response.getStatusLine());  
	        if (entity != null) {  
	            //打印响应内容的长度  
	      
	            System.out.println("Response content lenght:"  
	                    + entity.getContentLength());  
	            String content = EntityUtils.toString(entity);  
	              
	            //解决HttpClient获取中文乱码 ,用String对象进行转码  
	            System.out.println("Response content:"  
	                    + new String(content.getBytes("ISO-8859-1"),"UTF-8"));  
	        }  
	        System.err.println("==========================================================");  
	    } catch (Exception e) {  
	        // TODO: handle exception  
	    }finally{  
	        //关闭连接,释放资源  
	        httpClient.getConnectionManager().shutdown();  
	    }  
	}
}

三、使用JTidy

1、Tidy是W3C用来解析网页的一个软件包,可以方便地将HTML文档转化为符合XML标准的文档,由于XML可以方便地使用XSLT技术对内容进行抽取,所以使用Tidy配合XSLT可以方便地将各种网页的内容抽取出来,保存成我们需要的格式。

2、通过JTidy可以方便地将标准的HTML网页转换为XML的DOM对象,然后,通过XPath和XSTL将需要的内容抽取出来。

3、下载地址:http://sourceforge.net/projects/jtidy

4、JTidy的代码实现

package com.qianyan.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;

import org.w3c.dom.Document;
import org.w3c.tidy.Tidy;

public class TestJTidy {

	public static void main(String[] args) throws Exception {
		FileInputStream fis = new FileInputStream(new File("E:/baidu.htm"));
		InputStreamReader isr = new InputStreamReader(fis, "GBK");
		
		FileOutputStream fos = new FileOutputStream(new File("E:/baidu.xml"));
		Tidy tidy = new Tidy();
		tidy.setXmlTags(true);
		Document doc = tidy.parseDOM(isr, null);
		tidy.pprint(doc, fos);
		fos.close();
		fis.close();
		isr.close();
	}
}

四、使用Nekohtml

1、Nekohtml是一个Java语言的HTML扫描器和标签补全器(tag balancer),使得程序能解析HTML文档并用标准的XML借口来访问其中的信息。这个解析器能够扫描HTML文件并“修正”许多作者(人或机器)在编写HTML文档过程中常犯的错误。

2、Nekohtml能增补缺失的父元素、自动用结束标签关闭相应的元素,以及不匹配的内嵌元素标签。

NekoHTML的开发使用了Xerces Native Interface(XNI),后者是Xerces2的实现基础。

3、下载地址:http://nekohtml.sourceforge.net/index.html

4、代码实现:

package com.qianyan.test;

import java.io.BufferedReader;
import java.io.FileReader;

import org.cyberneko.html.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class TestNekoHTML {
    
	public static String textExtractor(Node root){
		   //若是文本的话,直接返回
		   if (root.getNodeType() == Node.TEXT_NODE) {
		    return root.getNodeValue().trim();
		   }
		   if(root.getNodeType() == Node.ELEMENT_NODE) {
		    Element elmt = (Element) root;
		    //抛弃脚本
		    if (elmt.getTagName().equals("STYLE")
		      || elmt.getTagName().equals("SCRIPT")
		      ||elmt.getTagName().equals("BR"))
		     return "";
		   
		    NodeList children = elmt.getChildNodes();
		    StringBuilder text = new StringBuilder();
		    for (int i = 0; i < children.getLength(); i++) {
		     text.append(textExtractor(children.item(i)));
		    }
		    return text.toString();
		   }
		   //对其他类型的节点,返回空值
		   return "";
		}
	
	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		DOMParser parser=new DOMParser();
		BufferedReader reader=new BufferedReader(new FileReader("e:/baidu1.htm"));
		parser.parse(new InputSource(reader));
		Document doc=parser.getDocument();
        Node body=doc.getElementsByTagName("body").item(0);
        String str=textExtractor(body);
        System.out.println(str);
        
	}

}


五、使用HTMLParser

1、HTML Parser是一个对HTML进行分析的快速实时的解析器。

2、用法:

1)Visitor

2))Filter

3、下载地址:http://htmlparser.sourceforget.net/

4、代码实现:

package com.qianyan.test;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.Tag;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.tags.ImageTag;
import org.htmlparser.tags.InputTag;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.tags.OptionTag;
import org.htmlparser.tags.SelectTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.htmlparser.visitors.NodeVisitor;
import org.htmlparser.visitors.ObjectFindingVisitor;
import org.junit.Ignore;
import org.junit.Test;

public class TestHtmlParser {

	/**
	 * 测试ObjectFindVisitor的用法
	 */
	@Test
//	@Ignore
	public void testImageVistor() {
		try {
			ImageTag imgLink;
			ObjectFindingVisitor visitor = new ObjectFindingVisitor(
					ImageTag.class);
			Parser parser = new Parser();
			parser.setURL("http://www.baidu.com");
			parser.setEncoding(parser.getEncoding());
			parser.visitAllNodesWith(visitor);
			Node[] nodes = visitor.getTags();

			for (int i = 0; i < nodes.length; i++) {
				imgLink = (ImageTag) nodes[i];
				StringBuilder sb = new StringBuilder();
				sb.append(" ImageURL = " + imgLink.getImageURL());
				sb.append("---- ImageLocation = " + imgLink.extractImageLocn());
				sb.append("--- SRC = " + imgLink.getAttribute("SRC"));
				System.out.println(sb.toString());

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 测试NodeVisitor的用法,遍历所有节点
	 */
	@Test
	@Ignore
	public void testVisitorAll() {
		try {
			Parser parser = new Parser();
			parser.setURL("http://www.baidu.com");
			parser.setEncoding(parser.getEncoding());
			NodeVisitor visitor = new MyNodeVisitor();
			parser.visitAllNodesWith(visitor);
		} catch (ParserException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 测试NodeClassFilter用法
	 */
	@Test
	@Ignore
	public void testLinkTag(){
		try{
			NodeFilter filter = new NodeClassFilter(LinkTag.class);
			Parser parser = new Parser();
			parser.setURL("http://www.baidu.com");
			parser.setEncoding(parser.getEncoding());
			NodeList list = parser.extractAllNodesThatMatch(filter);
			for(int i = 0; i < list.size(); i++){
				LinkTag node = (LinkTag) list.elementAt(i);
				System.out.println("Link is :" + node.extractLink());
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	/**
	 * 测试TagNameFilter用法
	 */
	@Test
	@Ignore
	public void testNodeFilter(){
		try{
			NodeFilter filter = new TagNameFilter("IMG");
			Parser parser = new Parser();
			parser.setURL("http://www.baidu.com");
			parser.setEncoding(parser.getEncoding());
			NodeList list = parser.extractAllNodesThatMatch(filter);
			for(int i = 0; i < list.size(); i++){
				System.out.println(" " + list.elementAt(i).toHtml());
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	/**
	 * 测试OrFilter的用法
	 */
	@Test
	@Ignore
	public void testOrFliter(){
		NodeFilter inputFilter = new NodeClassFilter(InputTag.class);
		NodeFilter selectFilter = new NodeClassFilter(SelectTag.class);
		NodeList  nodeList = null;
		try{
			Parser parser = new Parser();
			parser
			.setInputHTML("<head><title>OrFilter Test</title>"
		    + "<link href=http://www.baidu.com/test01/css.css’ text=’text/css’ rel=’stylesheet’ />"
		    + "<link href=http://www.baidu.com/test02/css.css’ text=’text/css’ rel=’stylesheet’ />"
		    + "</head>"
		    + "<body>"
		    + "<input type=’text’ value=’text1′ name=’text1′/>"
		    + "<input type=’text’ value=’text2′ name=’text2′/>"
		    + "<select><option id=’1′>1</option><option id=’2′>2</option><option id=’3′>3</option></select>"
		    + "<a href='http://www.baidu.com/'>baidu.com</a>"
		    + "</body>");
			
			parser.setEncoding(parser.getEncoding());
			OrFilter lastFilter = new OrFilter();
			lastFilter.setPredicates(new NodeFilter[]{selectFilter, inputFilter});
			nodeList = parser.parse(lastFilter);
			for(int i = 0; i <= nodeList.size(); i++){
				if(nodeList.elementAt(i) instanceof InputTag){
					InputTag tag = (InputTag)nodeList.elementAt(i);
					System.out.println("OrFilter tag name is :" + tag.getTagName()
							+ " ,tag value is:" + tag.getAttribute("value"));
				}
				if(nodeList.elementAt(i) instanceof SelectTag){
					SelectTag tag = (SelectTag)nodeList.elementAt(i);
					NodeList list = tag.getChildren();
					for(int j = 0; j < list.size(); j++){
						OptionTag option = (OptionTag)list.elementAt(j);
						System.out.println("OrFilter Option"
							+ option.getOptionText());
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}

}

/**
 * 内部实现了NodeVisitor下的visitTag方法
 * @author Administrator
 *
 */
class MyNodeVisitor extends NodeVisitor {
	public void visitTag(Tag tag) {
		System.out
				.println("Tag name is :" + tag.getTagName()
						+ "--- Class is :" + tag.getClass() + "---"
						+ tag.getText());
	}
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值