此搜索引擎适于在一个中等规模的局域网中使用,由于找到的网页存在数据库中,不仅可以索静态的HTML页面,可以搜索php、asp等动态页面。对于一个拥有5万个网页的系统(使用PII-400作为服务器),搜索响应时间在2-10秒左右,完全可以满足要求,由于Java、MySQL、PHP都是跨平台的软件,所以此搜索引擎不仅可以工作在Windows服务器上,而且也可以工作在Linux等其他系统中。
一、建立搜索引擎需要的数据库和数据表。
首先建立数据库:
c:\mysql\bin\> mysqladmin -uroot -pmypasswd create Spider
然后建立数据库中的表结构
c:\mysql\bin\> mysql -uroot -pmypasswd Spider < Spider.mysql
其中Spider.mysql为一个文本文件,其内容如下:
CREATE TABLE link (
Id int(10) unsigned NOT NULL auto_increment,
Url varchar(120) NOT NULL,
Class tinyint(3) unsigned NOT NULL default 0 ,
IsSearchLink tinyint(3) unsigned default 0,
PRIMARY KEY (Url),
UNIQUE Id (Id),
KEY Url (Url),
KEY Class (Class)
);
# 本局域网的初始主页地址,搜索蜘蛛从此网址开始搜索所有其他网页
INSERT INTO link VALUES( '1', 'HTTP://102.211.69.1/', '0', '0');
# 数据表 webpagelocal 用来存放下载的所有的网页
CREATE TABLE webpagelocal (
Id int(10) unsigned NOT NULL auto_increment,
Url varchar(120) NOT NULL,
Content text NOT NULL,
PRIMARY KEY (Url),
UNIQUE Id (Id),
KEY Url (Url)
);
# 数据表 webpagefindfast
# 用MakeFast.php从表webpagelocal中提取512字节的检索信息存放其中
CREATE TABLE webpagefindfast (
Id int(10) unsigned NOT NULL,
Url varchar(120) NOT NULL,
Title varchar(64),
Content blob,
PRIMARY KEY (Url),
KEY Url (Url),
KEY Title (Title)
);
二、以下为搜索网页和下载网页至本地数据库的Java程序LinkToDB.java,它也是此搜索引擎的核心和基础
/***************************** LinkToDB.java ***********************************
*
* 对URL中的http链接进行分析,将相对路径转换为绝对路径,排序方式输出结果到数据库
*
* 如果分析得到的URL是Link表中唯一的,就将其内容下载到表 WebPageLocal 中。
*
********************************************************************************
/
import java.io.*;
import java.util.*;
import java.net.*;
import java.lang.String;
import java.sql.*;
import java.text.*;
class Counter {
private int i = 1;
int read() { return i; }
void increment() { i++; }
}
public class LinkToDB {
String UrlHost = "";
String UrlFile = "";
String UrlPath = "";
static String StartWith = null;
boolean outsideTag = true; //判断是否在标记之中
static char[] buffer = new char[4096]; // 缓冲区:用于保存从 URL 读的数据
InputStreamReader read = null;
BufferedReader reader = null;
URLConnection uc = null;
private URL url = null;
private StreamTokenizer st;
private TreeMap counts = new TreeMap();//以排序方式保存找到的链接
LinkToDB(String myurl,String StartOnly){
try {
StartWith = StartOnly;
if(StartOnly!=null) { if(!myurl.startsWith(StartOnly)) return; }//只搜索此网站
url = new URL(myurl);
UrlHost = url.getHost();
UrlHost = UrlHost.toUpperCase();
UrlFile = url.getFile();
int v=UrlFile.lastIndexOf("/");
if(v!=-1) UrlPath = UrlFile.substring(0,v);
System.out.println("分析文件:"+myurl);
int uclength=200000;
int ucError=0;
try{
uc = url.openConnection();
uc.setUseCaches(false);
uc.connect();
}
catch(IOException io) { ucError=1; System.out.println("打不开待分析网页:"+myu
rl); }
if(ucError!=1){
uclength = uc.getContentLength();
if (uclength<200000) {
try{ read = new InputStreamReader(url.openStream()); }
catch(IOException io) {System.out.println("流打开错误:"+myurl);}
}
else System.out.println("文件太大,不分析");
}
if(read!=null){
reader=new BufferedReader(read);
if(reader!=null){
st = new StreamTokenizer(reader);
st.resetSyntax(); // 重置语法表
st.wordChars(0,255); // 令牌范围为全部字符
st.ordinaryChar('<'); // HTML标记两边的分割符
st.ordinaryChar('>');
}
}
}
catch(MalformedURLException e){ System.out.println("Malformed URL String!");}
}
void cleanup() {
try { read.close(); }
catch(IOException e) { System.out.println("流关闭错误"); }
}
void countWords() {
try {
while(st.nextToken()!=StreamTokenizer.TT_EOF) {
String s0="";
String s_NoCase="";
switch(st.ttype) {
case '<': //入标记字段
outsideTag=false;
continue; //countWords();
case '>': //出标记字段
outsideTag=true;
continue; //countWords();
case StreamTokenizer.TT_EOL: s0 = new String("EOL"); break;
case StreamTokenizer.TT_WORD: if(!outsideTag) s0 = st.sval; /*已经是字符
串*/ break;
default: s0 = "";// s0 = String.valueOf((char)st.ttype);/*单一字符*/
}
if(outsideTag) continue;//出了标记区域(<a >)
String s = "";
s_NoCase = s0.trim();
s0=s_NoCase.toUpperCase();
if(s0.startsWith("A ")||s0.startsWith("AREA ")||s0.startsWith("FRAME ")||s0.s
tartsWith("IFRAME ")){ //以这些开始的都是超级链接
int HREF_POS = -1;
if(s0.startsWith("FRAME ")||s0.startsWith("IFRAME ")) {
HREF_POS = s0.indexOf("SRC=");
s0 = s0.substring(HREF_POS+4).trim();
s_NoCase=s_NoCase.substring(HREF_POS+4).trim();
}
else {
HREF_POS=s0.indexOf("HREF=");
s0=s0.substring(HREF_POS+5).trim();
s_NoCase=s_NoCase.substring(HREF_POS+5).trim();
}
if(HREF_POS!=-1) {
if(s0.startsWith("\""))
{s0=s0.substring(1);s_NoCase=s_NoCase.substring(1);}
int QUOTE=s0.indexOf("\"");
if(QUOTE!=-1)
{s0=s0.substring(0,QUOTE).trim();s_NoCase=s_NoCase.substring(0,QUOTE).trim
();}
int SPACE=s0.indexOf(" ");
if(SPACE!=-1)
{s0=s0.substring(0,SPACE).trim();s_NoCase=s_NoCase.substring(0,SPACE).trim
();}
if(s0.endsWith("\""))
{s0=s0.substring(0,s0.length()-1);s_NoCase=s_NoCase.substring(0,s_NoCase.l
ength()-1);}
if(s0.indexOf("'")!=-1||s0.indexOf("JAVASCRIPT:")!=-1||s0.indexOf("..")!=-1
)
{s0="";s_NoCase="";} //有这些符号,认为非合法链接;两点表示上一目录,而我
只想向下级查找
if ( !s0.startsWith("FTP://") &&//以下后缀或前缀通常非网页格式
!s0.startsWith("FTP://") &&
!s0.startsWith("MAILTO:") &&
!s0.endsWith(".SWF") &&
!s0.startsWith("../")) //因../表示上一目录,通常只需考虑本级和下N级目录
s=s0;
if (!s.startsWith("HTTP://")&&!s.equals("")) {s=UrlHost+UrlPath+"/"+s;s_No
Case=UrlHost+UrlPath+"/"+s_NoCase;}
else if(s.startsWith("/")) {s=UrlHost+s;s_NoCase=UrlHost+s_NoCase;}
if(s.startsWith("HTTP://")) {s=s.substring(7);s_NoCase=s_NoCase.substring(
7);}
int JinHao=s.indexOf("#"); //如果含有"#"号,表示有效的链接是此前的部分
if(JinHao!=-1) {s=s.substring(0,JinHao).trim();s_NoCase=s_NoCase.substring(
0,JinHao).trim();}
int H=-1; //以下将/./转换为/
for(int m=0;m<4;m++){
H=s.indexOf("/./");
if(H!=-1) {s=s.substring(0,H)+s.substring(H+2);s_NoCase=s_NoCase.substring
(0,H)+s_NoCase.substring(H+2);}
}
int TwoXG=-1; //以下将//转换为/
for(int m=0;m<5;m++){
TwoXG=s.indexOf("//");
if(TwoXG!=-1) {s=s.substring(0,TwoXG)+s.substring(TwoXG+1);s_NoCase=s_NoCa
se.substring(0,TwoXG)+s_NoCase.substring(TwoXG+1);}
}
int OneXG=s.indexOf("/");
if(OneXG==-1) {s=s+"/";s_NoCase+="/";} //将xx.xx.xx.xxx转换为xx.xx.xx.xxx/的
标准形式