题目:基于网络爬虫的搜索引擎设计与实现
系 别:
专 业:计算机科学与技术
班 级:
学 号:
姓 名:
同组人:
指 导 教 师: 教师职称:
协 助 指 导 教 师: 教师职称:
摘要
本文从搜索引擎的应用出发,探讨了网络蜘蛛在搜索引擎中的作用和地住,提出了网络蜘蛛的功能和设计要求。在对网络蜘蛛系统结构和工作原理所作分析的基础上,研究了页面爬取、解析等策略和算法,并使用Java实现了一个网络蜘蛛的程序,对其运行结果做了分析。
关键字:爬虫、搜索引擎
Abstract
The paper,discussing from the application of the search engine,searches the importance and function of Web spider in the search engine.and puts forward its demand of function and design.On the base of analyzing Web Spider’s system strtucture and working elements.this paper also researches the method and strategy of multithreading scheduler,Web page crawling and HTML parsing.And then.a program of web page crawling based on Java is applied and analyzed.
Keyword: spider, search engine
一、项目背景
1.1搜索引擎现状分析
互联网被普及前,人们查阅资料首先想到的便是拥有大量书籍的图书馆,而在当今很多人都会选择一种更方便、快捷、全面、准确的方式——互联网.如果说互联网是一个知识宝库,那么搜索引擎就是打开知识宝库的一把钥匙.搜索引擎是随着WEB信息的迅速增加,从1995年开始逐渐发展起来的技术,用于帮助互联网用户查询信息的搜索工具.搜索引擎以一定的策略在互联网中搜集、发现信息,对信息进行理解、提取、组织和处理,并为用户提供检索服务,从而起到信息导航的目的.目前搜索引擎已经成为倍受网络用户关注的焦点,也成为计算机工业界和学术界争相研究、开发的对象.
目前较流行的搜索引擎已有Google, Yahoo, Info seek, baidu等. 出于商业机密的考虑, 目前各个搜索引擎使用的Crawler 系统的技术内幕一般都不公开, 现有的文献也仅限于概要性介绍. 随着W eb 信息资源呈指数级增长及Web 信息资源动态变化, 传统的搜索引擎提供的信息检索服务已不能满足人们日益增长的对个性化服务的需要, 它们正面临着巨大的挑战. 以何种策略访问Web, 提高搜索效率, 成为近年来专业搜索引擎网络爬虫研究的主要问题之一。
1.2课题开发背景
目前虽然有很多种搜索引擎,但各种搜索引擎基本上由三个组成部分:
(1)在互联网上采集信息的网页采集系统:网页采集系统主要使用一种工作在互联网上的采集信息的“网络蜘蛛”。“网络蜘蛛”实际上是一些基于web的程序,利用主页中的超文本链接遍历Web.利用能够从互联网上自动收集网页的“网络蜘蛛”程序,自动访问互联网,并沿着任何网页中的所有URL爬到其它网页,重复这过程,并把爬过的所有网页收集到网页数据库中。
(2)对采集到的信息进行索引并建立索引库的索引处理系统:索引处理系统对收集回来的网页进行分析,提取相关网页信息(包括网页所在URL、编码类型、页面内容包含的关键词、关键词位置、生成时间、大小、与其它网页的链接关系等),根据一定的相关度算法进行大量复杂计算,得到每一个网页针对页面内容中及超链中每一个关键词的相关度(或重要性),然后建立索引并存人到网页索引数据库中.索引数据库可以采用通用的大型数据库,如Oracle,Sybase等,也可以自己定义文件格式进行存放.为了保证索引数据库中的信息与Web内容的同步,索引数据库必须定时更新,更新频率决定了搜索结果的及时性.索引数据库的更新是通过启动“网络蜘蛛”对Web空间重新搜索来实现的.
(3)完成用户提交查询请求的网页检索器:网页检索器一般是一个在Web服务器上运行的服务器程序,它首先接收用户提交的查询条件,根据查询条件对索引库进行查找并将查询到的结果返回给用户.当用户使用搜索引擎查找信息时,网页检索器接收用户提交的关键词,由搜索系统程序从网页索引数据库中找到符合该关键词的所有相关网页.有的搜索引擎系统综合相关信息和网页级别形成相关度数值,然后进行排序,相关度越高,排名越靠前.最后由页面生成系统将搜索结果的链接地址和页面内容摘要等内容组织起来返回给用户.典型的搜索引擎系统如Google就是采用这种策略.
信息的飞速增长,使搜索引擎成为人们查找信息的首选工具,Google、百度、中国搜索等大型搜索引擎一直是人们讨论的话题.搜索引擎技术的研究,国外比中国要早近十年,从最早的Archie,到后来的Excite,以及ahvista、overture、google等搜索引擎面世,搜索引擎发展至今,已经有十几年的历史,而国内开始研究搜索引擎是在上世纪末本世纪初.在许多领域,都是国外的产品和技术一统天下,特别是当某种技术在国外研究多年而国内才开始的情况下.例如操作系统、字处理软件、浏览器等等,但搜索引擎却是个例外.虽然在国外搜索引擎技术早就开始研究,但在国内还是陆续涌现出优秀的搜索引擎,像百度、中搜等.
随着搜索引擎技术的成熟,它将成为获取信息、掌握知识的利器.但是现有的搜索引擎对于用户所提出的查询要求仅限于关键词的简单逻辑组合,搜索结果重视的是返回的数量而不是质量,在结果文档的组织和分类上也有所欠缺.国外的一次调查结果显示,约有71%的人对搜索的结果感到不同程度的失望.因此,如何提高搜索引擎的智能化程度,如何按照知识应用的需要来组织信息,使互联网不仅提供信息服务,而且能为用户提供知识服务,将成为计算机工业界和学术界有待研究的方向。
1.3网络爬虫的工作原理
网络爬虫是搜索引擎的核心部分,其名称出自Spider 的意译, 具有相同词义的词语还有Crawler, robo ts, bot s, wanderer 等等.网络爬虫定义有广义和狭义之分, 狭义上的定义为利用标准的http 协议根据超级链接和Web 文档检索的方法遍历万维息空间的软件程序; 而广义则是所有能利用http 协议检索Web 文档的软件都称之为网络爬虫.网络爬虫是一个功能很强的自动提取网页的程序, 它为搜索引擎从万维网上下载网页, 是搜索引擎的重要组成. 它通过请求站点上的HTML 文档访问某一站点. 它遍历W eb 空间, 不断从一个站点移动到另一个站点, 自动建立索引, 并加入到网页数据库中. 网络爬虫进入某个超级文本时, 它利用HTML语言的标记结构来搜索信息及获取指向其他超级文本的U RL 地址, 可以完全不依赖用户干预实现网络上的自动“爬行”和搜索。
二、系统开发工具和平台
2.1关于java语言
Java语言是由Sun公司于1995年推出的一种新的编程语言,它是一种跨平台、适合于分布式计算环境的纯面向对象语言。Java语言及其扩展正在逐步成为互联网应用的规范,掀起了自PC机以来的又一次技术。一般认为,B语言导致了C语言的诞生、C语言演变出C++语言,而Java语言则明显带有C++语言的特征。Java总是和C++联系在一起,而C++则是从C语言派生而来的,所以Java语言继承了这两种语言的大部分特性。Java的语法是从C继承的,Java许多面向对象特性都受到C++的影响。事实上,Java中几个自定义的特性都来自于或可以追溯到它的这些前驱语言。略有不同的是,Java语言完全面向对象,从而摒弃了二者的不足之处。Java语言的诞生与过去约30年中计算机语言的不断改进和发展密切相关。
Java是由James Gosling、Patrick Naughton、Chris Warth、Ed Frank以及Mike Sheridan等人于1991年在Sun Microsystems公司设计出来的,开发第一个版本花了18个月时间。该语言最初名叫“Oak”,后来发现“Oak”已经是Sun公司另外一种语言的注册商标,于1995年更名为“Java”,即太平洋上一个盛产咖啡的岛屿的名字。从1992 的秋天Oak问世,到1995春天公开发布Java语言,许多人都对Java的设计和改进做出了贡献。
自从于1995年被正式推出之后,Java语言就以其独特的优势迅猛发展,经过短短8、9年时间,成为迄今为止最为优秀的面向对象语言。Java也从当初的一种语言而逐渐形成一种产业,基于Java语言的J2EE架构已成为微软.NET平台的强大竞争对手。当初,Java语言最初的发布不亚于一场,但是它并不标志着Java快速革新时代的结束。在Java 1.0发布后不久,Java的设计者就已经制定出了Java 1.1、 Java 1.2、 Java 1.3、 Java 1.4 、Java 2、Java 2.1.4版。
作为当前一种被广泛使用的面向对象编程语言,Java具有多方面的特点。如果与其他众多的编程语言作一下比较,会发现这些特点正是Java语言之所以如此风靡的原因所在。虽然Java在某些方面(例如资源耗费)也存在一些不足,但这丝毫不影响Java作为目前最优秀面向对象编程语言的地位。 Java是一种被广泛使用的网络编程语言,这是一种新的计算概念。网络环境下的编程语言最需要解决的是可移植性和安全性问题。以字节方式进行编码,使得程序不受运行平台和环境的成为可能。Java语言还提供了丰富的类库,使程序设计人员可以很方便地调用相关类建立起自己的系统。Java作为一种高级程序设计语言,它除具有面向对象、编写简单、脱离机器结构、具有分布性、鲁棒性、可移植性、安全性特点外,并且提供了并发机制,解释执行具有很高的性能。
2.2 Jbuilder介绍
Java的开发工具中,最出名的莫过于Borland公司的JBuiIder了。对于一些没有弄清楚开发工具与JDK的区别的Java入门者来说。JBuiIder就如同Visual c++之于c++,以为JBuiIder就是Java的全部。比起捆绑在服务器上销售的JDeveloper,JBuiIder应该是唯一的仅靠自身的实力而占领了大部分市场的Java商用开发工具了。Jbuilder的特点::
1)Jbuilder支持最新的Java技术,包括Applets、JSP/Servlets、JavaBean以及EJB(Enterprise JavaBeans)的应用。
2)用户可以自动地生成基于后端数据库表的EJB Java类,Jbuilder同时还简化了EJB的自动部署功能.此外它还支持CORBA,相应的向导程序有助于用户全面地管理IDL(分布应用程序所必需的接口定义语言Interface Definition Language)和控制远程对象。
3)Jbuilder支持各种应用服务器。Jbuilder与Inprise Application Server紧密集成,同时支持WebLogic Server,支持EJB 1.1和EJB 2.0,可以快速开发J2EE的电子商务应用。
4)Jbuilder能用Servlet和JSP开发和调试动态Web 应用。
5)利用Jbuilder可创建(没有专有代码和标记)纯Java2应用。由于Jbuilder是用纯Java语言编写的,其代码不含任何专属代码和标记,它支持最新的Java标准。
6)Jbuilder拥有专业化的图形调试介面,支持远程调试和多线程调试,调试器支持各种JDK版本,包括J2ME/J2SE/J2EE。 JBuilder环境开发程序方便,它是纯的Java 开发环境,适合企业的J2EE开发。
因此本次开发使用Jbuilder 2006.
2.3 servlet的原理
Servlet是指运行在服务器端的Java小程序。用于响应客户端的请求。在默认情况下,Servlet采用一种无状态的请求-响应处理方式。Servlet代码的主要作用是为了增强Java服务器端的功能,它运行在服务器端,用于接收并且处理浏览器客户端发出的请求,该请求是通过配置文件web.xml的相关配置进行转发。也就是说Servlet是一个标准的Java类,它符合Java类的一般规则。和一般的Java类不同之处只是在于Servlet可以处理Http请求。
1.servlet是持久的。servlet只需Web服务器加载一次,后续又用到这个servlet,就不需要再加载。(所谓加载是指servlet加载进JVM运行)
2.servlet是与平台无关的。
3.servlet是可扩展的。
ActionServlet继承自javax.servlet.http.HttpServlet类,其在Struts framework中扮演的角色是中心控制器。它提供一个中心位置来处理全部的终端请求。控制器ActionServlet主要负责将HTTP的客户请求
信息组装后,根据配置文件的指定描述,转发到适当的处理器Action。
Servlet的原理图描述如下:
使用servlet有几个优点:
一是有效性,servlet的初始化代码仅在web服务器第一次加载时候执行一次,一旦加载了servlet,在处理一个新的请求的时候,只须调用一个新的服务方法。与处理每个请求都要全部加载一个完整的可执行程序相比,效率得以提高。
二是稳定性,servlet能够维护每个请求的状态,一旦加载了servlet,她就驻留在内存中,对收到的请求提供服务。
三是可移植性,servlet是用java开发的,因此它是可移植的,这种可移植性使servlet能够移植到新的操作系统中而不必改变代码。
四是安全性,servlet在服务器端运行,因此,安全性由web服务器提供能保障,servlet也能够利用java Security Manager提供的安全性功能。
三、系统总体设计
3.1系统总体结构
3.2系统类图
结构
2)网络爬虫结构
3)页面解析结构
搜索策略
网络爬虫在搜索时往往采用一定的搜索策略。
一是宽度或深度优先搜索策略:搜索引擎所用的第一代网络爬虫主要是基于传统的图算法, 如宽度优先或深度优先算法来索引整个Web, 一个核心的U RL 集被用来作为一个种子集合, 这种算法递归的跟踪超链接到其它页面, 而通常不管页面的内容, 因为最终的目标是这种跟踪能覆盖整个W eb. 这种策略通常用在通用搜索引擎中,因为通用搜索引擎获得的网页越多越好, 没有特定的要求.
二是宽度优先搜索算法(又称广度优先搜索) 是最简便的图的搜索算法之一, 这一算法也是很多重要的图的算法的原型.单源最短路径算法和P rim 最小生成树算法都采用了和宽度优先搜索类似的思想.宽度优先搜索算法是沿着树的宽度遍历树的节点, 如果发现目标, 则算法中止. 该算法的设计和实现相对简单, 属于盲目搜索. 在目前为覆盖尽可能多的网页, 一般使用宽度优先搜索方法. 也有很多研究将宽度优先搜索策略应用于聚焦爬虫中. 其基本思想是认为与初始U RL 在一定链接距离内的网页具有主题相关性的概率很大. 另外一种方法是将宽度优先搜索与网页过滤技术结合使用, 先用广度优先策略抓取网页, 再将其中无关的网页过滤掉. 这些方法的缺点在于, 随着抓取网页的增多, 大量的无关网页将被下载并过滤, 算法的效率将变低。
三是深度优先搜索所遵循的搜索策略是尽可能“深”地搜索图. 在深度优先搜索中, 对于最新发现的顶点, 如果它还有以此为起点而未探测到的边, 就沿此边继续汉下去. 当结点v 的所有边都己被探寻过, 搜索将回溯到发现结点v 有那条边的始结点. 这一过程一直进行到已发现从源结点可达的所有结点为止. 如果还存在未被发现的结点, 则选择其中一个作为源结点并重复以上过程, 整个进程反复进行直到所有结点都被发现为止. 深度优先在很多情况下会导致爬虫的陷入( t rapped) 问题, 所以它既不是完备的, 也不是最优的。
四、系统详细设计
4.1搜索引擎界面设计
界面设计实现
设计界面如下:
设计代码分析
4.2 servlet的实现
用Servlet来响应用户的请求,实现搜索参数的传入。具体代码设计为:
package crawer;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.net.*;
public class MyServlet extends HttpServlet {
private static final String CONTENT_TYPE = "text/html; charset=GBK";
public Timer timer;
myspider crawler ;
ArrayList< String> myresult;//搜索到的结果
//Initialize global variables
public void init() throws ServletException {
}
//Process the HTTP Get request
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
String var0 = request.getParameter("param0");
if (var0 == null) {
var0 = "";
}
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
byte[] bytes=var0.getBytes("ISO-8859-1");
String search=new String(bytes,"GB2312");
crawler = new myspider("http://www.hao123.com",10,search);
//Thread search=new Thread(crawler);
// search.start();
//此处开始爬行
crawler.run();
//启动定时器,在时间内检查是否有结果,并显示
myresult=new ArrayList< String>(); //搜索到的结果
myresult=crawler.getResult();
out.println("");
out.println("
out.println("
注意默认起始站点为:http://www.hao123.com,层数为10
");out.println("
搜索"+search+"结果:
");out.println("
");String te;
for(int i=0;i out.println(" "+te+"、"+myresult.get(i)+" } if(myresult.size()==0){ out.println(" 对不起,没有找到结果 } out.println(""); out.println(""); out.close(); } //Clean up resources public void destroy() { } } 4.3网页的解析实现 4.3.1网页的分析 网页文档作为一种半结构化文本是一种界于自由文本和结构化文本之间的数据,它通常没有严格的格式。对于这类文本一般是通过分析文本中特有的标志性字符进行爬行处理,具体而言就是分析HTML语言中的各种标记之间的关系。网页信息的载体是网页文本,用超文本标记语言编写。由HTML标准定义了一组元素类型,不同类型的元素分别描述文本、图像和超文本链接等。一个元素的描述一般由开始标记(Start Tag)、内容(Content)、结束标记(End Tag)所组成。元素名称出现在开始标记中,在HTML语言中标记为<元素名称>,对应的结束标记为</元素名称>,内容出现在开始标记和结束标记之间。通过构造网页标记树的方法可反映网页的结构特点,下图是一个简单的动态网页标记树 4.3.2网页的处理队列 页面处理队列中保存的是页面的URL,它实际上是由等待队列、处理队列、错误队列、完成队列组成。正是通过它们,某个具体的移动Spider得以完成对该Spider所对应web的全部搜索任务。页面队列中保存的页面的URL都是属于内链接。 (1)等待队列(WaitURL)。在这个队列中,URL等待被移动Spider程序处理。新发现的URL被加入到这个队列中。 (2)处理队列(Proces—sUI )。当移动Spider程序开始处理URL时,它们被传送到这一队列,但同一个URL不能被多次处理,因为这样是浪费资源。当一个URL被处理过后,它将被移送到错误队列或者是完成队列。 (3)错误队列(ErrorURL)。如果在处理某一页面时发生错误,它的URL将被加入到错误队列,该URL到达这一队列后将不再移人其他队列。一旦网页移入错误队列,移动Spider程序将不会再对它作进一步处理。 (4)完成队列(LaunchURL)。如果在处理网页时没有发生错误,处理完毕时,该URL将被加入到完成队列,该URL到达这一队列后将不再移人其他队列。 同一时间一个URL只能在一个队列中,这也叫做URL的状态,这是因为人们常常使用状态图描述计算机程序,程序按照状态图从一个状态变换到下一个状态实际上,当发现URL(内链接)时,移动Spider会检查该URL是否已经存在于完成队列或错误队列中,如果已经存在于上述两种队列的任何一个队列中,那么移动Spider将不会对此URL进行任何处理。由此,可避免某个页面被重复处理,防止陷入死循环。 4.3.3 搜索字符串的匹配 对于要搜索的字符串,必须在抓取的网页中进行匹配检查,如果存在于该网页中,则把地址添加到输出队列中。 4.3.4网页分析类的实现 package crawer; //html文件解析类 import java.io.ByteArrayOutputStream; import java.util.Hashtable; import java.util.Enumeration; import java.util.Vector; import java.util.Stack; import java.net.URL; import java.net.MalformedURLException; import java.util.StringTokenizer; import java.io.InputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; //类实体 public class HtmlParser { ArrayList< String> fafa=new ArrayList< String>(); URL base = null; // 基本URL public HtmlParser (String PageContent) { int state = 0; StringBuffer sb = new StringBuffer(); int i = PageContent.length(); //System.out.println("循环读取解析"); for (int j = 0; j < i; j++) { //循环读取解析/ switch (state) { case 0: if (PageContent.charAt(j) == '<') state = '<'; break; case '<': if (PageContent.charAt(j) == '>') { state = 0; analyze(sb.toString()); sb.setLength(0); } else { sb.append((char) PageContent.charAt(j)); } } } } public void analyze(String param) { StringTokenizer st = new StringTokenizer(param); if (st.countTokens() < 2) return; String first_word = st.nextToken().toLowerCase(); if (first_word.equals("a")) { analyzeAnchor(st.nextToken("")); } else if (first_word.equals("frame")) { analyzeFrame(st.nextToken("")); } else if (first_word.equals("base")) { extractBase(st.nextToken("")); } } /**分析 分析. */ void analyzeAnchor(String anchor) { String href = extract(anchor, "href"); if (href == null) return; addURL( href); } /**分析 分析. */ void analyzeFrame(String frame) { String src = extract(frame, "src"); if (src == null) return; addURL(src); } /** 由 void extractBase(String b) { String b2 = extract(b, "href"); if (b2 != null) { try { base = new URL( b2); } catch (MalformedURLException e) { e.printStackTrace(); } } } String extract(String line, String key) { try { key = key.toLowerCase(); String lower_case = line.toLowerCase(); int i = lower_case.indexOf(key); if (i < 0) return null; i += key.length(); if (line.charAt(i) != '=') return null; i++; int i2; if (line.charAt(i) == '"') { i++; i2 = line.indexOf('"', i); if (i2 < 0) { return line.substring(i); } else { return line.substring(i, i2); } } else { int targ = line.length(); for (i2 = i; i < targ; i++) { if (Character.isSpace(line.charAt(i))) break; } return line.substring(i, i2); } } catch (StringIndexOutOfBoundsException e) {} return null; } /** 添加 "url" 到URL列表. */ public void addURL(String url) { //System.out.println(url); fafa.add(url); } public ArrayList< String> getResult(){ return fafa; } } 4.4网络爬虫的实现 爬虫结构分析 网络爬虫沿着WWW文件间的链接在网上漫游,记录URL、文件的简明概要、关键字或索引。其漫游结果是形成一个很大的本地数据库,你可以通过WWW浏览器访问与该网络爬虫相配合的检索服务器对其结果进行查询。但并不是所有的检索服务器都采用robot只有那些自动在网上漫游并形成自己的数据库的那些才是。每个robot完成的功能都不一样所以它们的本地索引结果也就不同。Robot的运行方式是这样的:从一个或一组URL开始,访问该URL并进行本地索引同时记录该URL所指HTML文件中所有新的URL锚链(anchor);然后再以这些新的URL为起始点,继续进行本地索引,直到再没有满足条件的新URL为止。在记录新URL时,可以进行分析和判断,从中去掉不需要或不想要的URL,这不但提高了本地索引的速度,也减少了索引文件在本地所占用的磁盘空间。虽然robot和spider 功能很强,但如果有一组URL地址没有被组~bURL所链接到,那 么robot和spider就找不到它们。同时由于robot和spider不能更新太快(因为网络带宽有限,如果更新太快,那么其他用户就会受到影响),难免有不能及时加入的新WWW地址,所以很多拥有robot和spider的WWW索引和检索服务站点同时提供一项由用户加入新WWW地址的功能。如果仅仅是从远程获得数据,实现一个robot并不很难。但由于每个robot都是与一定的索引和检索技术相联系的,所以它必须要能与其它模块相配合工作。因而其实现时要考虑很多相关技术。一般来说,一个索引和检索服务器在实现时要涉及的主要技术有如下几方面: (1)HTTP (HyperText Transfer Protoco1)协议。它是WWW上数据传输的标准协议。通过它,我们可以跟WWW服务器进行信息交换:从服务器获得我们所要的各种信息,并将我们的要求发给服务器。 (2)HTML(HyperText Markup Language)语言。它是WWW服务器所发回各种数据的主要描述语言, 因为搜索引擎的主要搜索目标是文本,所以必须对HTML进行解析,提取出相应的数据。 (3)分词技术。为了提取关键字或者知识,必须分隔出单个的词和句子。(4)公共网关接口CGI(Conlmon Gateway Interface)。通过它,我们可以执行WWW服务器上的程序:我们把查询要求传递给HTTP服务器,HTTP~务器根据客户的请求执行CGI程序CG I程序根据通过HTTP服务器传递的查询要求对数据库进行操作,并把查询结果以HTML的形式传递回HTTP客户。 爬虫的设计实现 package crawer; import java.util.*; import java.net.*; import java.io.*; import java.util.regex.*; import javax.servlet.http.HttpServletResponse; public class myspider implements Runnable{ private HashMap< String,ArrayList< String>> disallowListCache = new HashMap< String,ArrayList< String>>(); ArrayList< String> errorList= new ArrayList< String>();//错误信息 ArrayList< String> result=new ArrayList< String>(); //搜索到的结果 String startUrl;//开始搜索的起点 int maxUrl;//最大处理的url数 String searchString;//要搜索的字符串(英文) boolean caseSensitive=false;//是否区分大小写 boolean limitHost=false;//是否在的主机内搜索 //PrintWriter out ; public myspider(String startUrl,int maxUrl,String searchString){ this.startUrl=startUrl; this.maxUrl=maxUrl; this.searchString=searchString; //输出设定 } public ArrayList< String> getResult(){ return result; } public void run(){//启动搜索线程 crawl(startUrl,maxUrl, searchString,limitHost,caseSensitive); } //检测URL格式 private URL verifyUrl(String url) { // 只处理HTTP URLs. if (!url.toLowerCase().startsWith("http://")) return null; URL verifiedUrl = null; try { verifiedUrl = new URL(url); } catch (Exception e) { return null; } return verifiedUrl; } // 检测robot是否允许访问给出的URL. private boolean isRobotAllowed(URL urlToCheck) { String host = urlToCheck.getHost().toLowerCase();//获取给出RUL的主机 //System.out.println("主机="+host); // 获取主机不允许搜索的URL缓存 ArrayList< String> disallowList =disallowListCache.get(host); // 如果还没有缓存,下载并缓存。 if (disallowList == null) { disallowList = new ArrayList< String>(); try { URL robotsFileUrl =new URL("http://" + host + "/robots.txt"); BufferedReader reader =new BufferedReader(new InputStreamReader(robotsFileUrl.openStream())); // 读robot文件,创建不允许访问的路径列表。 String line; while ((line = reader.readLine()) != null) { if (line.indexOf("Disallow:") == 0) {//是否包含"Disallow:" String disallowPath =line.substring("Disallow:".length());//获取不允许访问路径 // 检查是否有注释。 int commentIndex = disallowPath.indexOf("#"); if (commentIndex != - 1) { disallowPath =disallowPath.substring(0, commentIndex);//去掉注释 } disallowPath = disallowPath.trim(); disallowList.add(disallowPath); } } // 缓存此主机不允许访问的路径。 disallowListCache.put(host, disallowList); } catch (Exception e) { return true; //web站点根目录下没有robots.txt文件,返回真 } } String file = urlToCheck.getFile(); //System.out.println("文件getFile()="+file); for (int i = 0; i < disallowList.size(); i++) { String disallow = disallowList.get(i); if (file.startsWith(disallow)) { return false; } } return true; } private String downloadPage(URL pageUrl) { try { // Open connection to URL for reading. BufferedReader reader = new BufferedReader(new InputStreamReader(pageUrl.openStream())); // Read page into buffer. String line; StringBuffer pageBuffer = new StringBuffer(); while ((line = reader.readLine()) != null) { pageBuffer.append(line); // System.out.println(line);//输出网页测试,结果可以显示 } return pageBuffer.toString();//返回整个网页字符 } catch (Exception e) { } return null; } // 从URL中去掉"www" private String removeWwwFromUrl(String url) { int index = url.indexOf("://www."); if (index != -1) { return url.substring(0, index + 3) + url.substring(index + 7); } return (url); } // 解析页面并找出链接 private ArrayList< String> retrieveLinks(URL pageUrl, String pageContents, HashSet crawledList, boolean limitHost) { ArrayList< String> linkList = new ArrayList< String>(); HtmlParser parser=new HtmlParser(pageContents); linkList=parser.getResult(); return (linkList); } // 搜索下载Web页面的内容,判断在该页面内有没有指定的搜索字符串 private boolean searchStringMatches(String url,String pageContents, String searchString, boolean caseSensitive){ String searchContents = pageContents; if (!caseSensitive) {//如果不区分大小写 searchContents = pageContents.toLowerCase(); } //Pattern p = Pattern.compile("[\\\\s]+"); // String terms = p.split(searchString).toString(); String terms =searchString; //for (int i = 0; i < terms.length(); i++) { if (caseSensitive) { if (searchContents.indexOf(terms) == -1) { return false; } else{ int aa=searchContents.indexOf(terms); String bb=searchContents.substring(aa,aa+terms.length()+10); // System.out.println(bb); result.add(""+bb+""); } } else { if (searchContents.indexOf(terms.toLowerCase()) == -1) { return false; } else{//存在相同字符串 int aa=searchContents.indexOf(terms.toLowerCase()); String bb=searchContents.substring(aa,aa+terms.length()+10); // System.out.println(bb); result.add(""+bb+""); } } } return true; } //执行实际的搜索操作 public ArrayList< String> crawl(String startUrl, int maxUrls, String searchString,boolean limithost,boolean caseSensitive ) { System.out.println("searchString="+searchString);//搜索字符串 HashSet< String> crawledList = new HashSet< String>(); LinkedHashSet< String> toCrawlList = new LinkedHashSet< String>(); if (maxUrls < 1) { errorList.add("Invalid Max URLs value."); System.out.println("Invalid Max URLs value."); } if (searchString.length() < 1) { errorList.add("Missing Search String."); System.out.println("Missing search String"); } if (errorList.size() > 0) { System.out.println("err"); return errorList; } // 从开始URL中移出www startUrl = removeWwwFromUrl(startUrl); toCrawlList.add(startUrl); while (toCrawlList.size() > 0) { if (maxUrls != -1) { if (crawledList.size() == maxUrls) { break; } } // Get URL at bottom of the list. String url = toCrawlList.iterator().next(); // Remove URL from the to crawl list. toCrawlList.remove(url); // Convert string url to URL object. URL verifiedUrl = verifyUrl(url); // Skip URL if robots are not allowed to access it. //if (!isRobotAllowed(verifiedUrl)) { // continue; // } // 增加已处理的URL到crawledList crawledList.add(url); //System.out.println("提示搜索过的:"+verifiedUrl);//提示搜索过的url String pageContents = downloadPage(verifiedUrl); if (pageContents != null && pageContents.length() > 0){ // 从页面中获取有效的链接 //ArrayList< String> links =retrieveLinks(verifiedUrl, pageContents, crawledList,limitHost); HtmlParser parser=new HtmlParser(pageContents); ArrayList< String> links=parser.getResult(); //links.add("test message!"); // for(int j=0;j // } toCrawlList.addAll(links);//添加新取得的连接 if (searchStringMatches(url,pageContents, searchString,caseSensitive)) { //result.add(url); System.out.println("该字段存在于:"+url);//输出找到的地址 } } } return result; } // 主函数 public static void main(String[] args) { if(args.length!=3){ System.out.println("Usage:java SearchCrawler startUrl maxUrl searchString"); return; } int max=Integer.parseInt(args[1]); myspider crawler = new myspider(args[0],max,args[2]); Thread search=new Thread(crawler); System.out.println("Start searching..."); System.out.println("result:"); search.start(); /**/ } } 五、系统测试 搜索测试以默认开始网页作为起始页面,输入搜索字符串:百度,如下图所示: 点击搜索,开始执行。执行完毕,出现结果: 搜索成功。 六、结论 本系统开发过程中用到了许多学过的知识,具体说来有数据结构、java语言程序设计、软件工程、优化理论等等。在编程中发现这些学科相互联系,相辅相成,在以后更加复杂的系统中肯定会涉及到更多、更复杂的学科,需要我们认真学习和掌握的东西实在是太多了。 本软件只是对搜索引擎功能的基本实现,在技术方面还存在许多不足之处。当然在这突飞猛进的信息时代,技术的更新更是日新月异,所以其中有的思想不可能完全适应于各种实际情况。由于本人学习软件工程的时间不长,程序的设计方面不够规范,有些简单的想法却用了很长的代码来实现造成了代码冗余,还有部分想法没有实现。我将在今后的学习中不断完善自己。 致谢 当这篇论文最终完成的时候,我要向给予过我热情帮助和悉心指导的老师和师兄们致以真诚的谢意。 首先,我要感谢我的导师,感谢他带给我来学习的机会,感谢他对我学术上的悉心指导,感谢他对我生活上的关怀和体贴。导师是不仅是我学业上的导师,更是生活中让我敬佩的学者、长者。给我留下深刻印象的,是他知识的渊博、治学态度的严谨、诲人不倦的学者风范,是他谦逊待人、处处关心别人的长者风度,是他勤奋忘我的工作态度、精益求精的治学作风。特别是老师做大事的风范和气度,尤其让我钦佩。这里我要再次感谢老师。 在本文的最后,我要再次感谢我的导师,同时也向与老师一样辛勤育人,无私付出的各位导师、师长致以深深的谢意。 参考文献 [1] 李晓明,闷宏飞,王继民.搜索引擎— —原理、技术与系统[M].北京:科学出版社,2004. [2] Heaton J.网络机器人Java编程指南[M].北京:电子工业出版社,2002. [3] 崔泽永,常晓燕.搜索引擎的Web Robot的技术与优化[J].微机发展,2004,14(4):100—102. [4] Shafer C.数据结构与算法分析(JAVA版)[M].北京:电子工业出版社,2002. [5]贾年.基于移动Agent搜索引擎的研究与实现[D].成都:电子科技大学,2005. [6]贾年.移动Agent研究[J].四川工业学院学报,2004,23(3):51—54. [7]S.Charkabarti.Mimng the Web’s Link structure[J].IEEE Computer,2002,32(8):60—67. [8]徐宝文,张卫丰.搜索引擎与信息获取技术【M】.北京:清华大学出版社,2003