标题如何创建自己的搜索引擎
栏目评测与市场
作者翁建元
发布2001年7期
编写小的搜索引擎实际上非常简单,只写几行Perl程序就可以把你自己的搜索引擎加到你的网点上。你所连的服务器必须运行UNIX系统,而且你要具有安装CGI脚本的能力。
一、提出一个算法
创建一个倒序索引(或称倒序索引文件)。这是一个文字表单,非常像书末尾部分的索引。假定我们有一个非常简单的网站,它只包含两页,如下所示:
one.html:
<html><head>
<title>Doc One</title>
</head><body>
<p>Here document one.
</body></html>
two.html:
<html><head>
<title>Doc Two</title>
</head><body>
<p>Here another document.
</body></html>
为了对此网点进行索引,我们需要生成两个表单。首先,我们将每个页面进行编号,并列出每页的标题和URL。这样,以后我们就可以拿数字来代表页面,会省出不少空间。
1 => /one.html,“Doc One”
2 => /two.html,“Doc Two”
下一步,我们通过列出每个字及其所在的文件来生成倒序索引:
another=>2
doc=>1,2
document=>1,2
here=>2
is=>1,2
one=>1
this=>1
two=>2
要想实现搜索,在倒序索引中查出你要找的字,然后查看这个字后面列出的网页。当你想搜索“here”这个字时,我们的脚本将在倒序索引中查找“here”,得到“2”,再去看“2”代表的网页,并把关于此文件的信息以链接的形式显示出来
<a href=“/two.html”>Doc Two</a>
同样,如果你键入两个字,脚本将把两个字都查找一遍,并且都列出把两个字都包含在内的网页。
二、决定数据结构
将倒序索引存在普通的文件中,并且用grep对它进行搜索来查找文字并不困难。因为索引比整个网点要小,所以此方法比用find和grep来搜索整个网点要有所进步。
我们用DBM文件,当网页表单和索引文件中只含有(name=>value)类型的记录时,可以很容易就将它们在DBM文件的字符串中定位。用Perl编写的例索引如下所示:
%dbm =(
'-1' => '<a href=“/one.html”>Doc One</a>',
'-2' => '<a href=“/two.html”>Doc Two</a>',
'another' => '-2',
'doc' => '-1-2',
'document' => '-1-2',
'here' => '-2',
'is' => '-1-2',
'one' => '-1',
'this' => '-1',
'two' => '-2'
);
三、创建一个索引文件
现在我们要编两个脚本:读取你网站上的所有文件和创建倒序索引(索引文件)的代码,以及查找用户在查找表中输入的字的CGI脚本。我们先来写索引文件。
首先,我们打开将要存储倒序索引的DBM文件。我将使用Berkeley DB来完成,这样打开索引文件:
use DB_File
dbmopen(%db,“search_index.db”, 0644) or die“dbmopen: $!”;
当然,在UNIX中查找文件的最简单的方法是利用UNIX find命令。
open(FILES, “find . -name '*.html' -print|”) or die“open for find: $!”;
我们逐个打开HTML并把它们的内容放在一个变量中:
my $filename;
while(defined($filename = <FILES>))
{
print “indexing $filename”;
chop $filename;
open(HTML, $filename) or do{warn“open $filename: $!”; next;};
my $html = join('', <HTML>);
close HTML;
然后用规则表达式取出标题并在网页表单中为此页建一个入口
my ($title) =($html =~ /<title>([^<]*)/i);
$title = $filename if(!defined $title);
$db{--$fileno} = “<a href=\”$filename\“>$title</a>”;
现在我们要列出网页上所有的字,首先我们去掉HTML标签:
$html=~s/<[^>]+>//g;
如果我们的搜索对大小写不敏感,那么把所有文字存成同样的字体将简化查询,现在把文件都换成小写字体:
$html=~ tr/A-Z/a-z/;
下面,我们要把文件中所有字都列出:
my@words=($html=~/ \w+/g);
最后,我们把这个字加入倒序索引文件中相应的行,确保同一个字没有索引两遍:
my $last = “”;
for (sort @words){
next if($_ eq $last);
$last = $_;
$db{$_} = defined $db{$_} ? $db{$_}.$fileno: $fileno;}
基本上就是这样,这是整个脚本。当你在网点上运行它时,它会在你网点的高层目录中生成一个名为“search_index.db”的文件。这个文件包含有你网点上所有字的索引。
四、创建搜索用的CGI
我们已有一个索引,现在该考虑让用户使用它。我将执行一次简单的搜索,寻找包含用户输入的所有字的网页。搜索表非常简单:
<form action=“/search.cgi”>
<p><input name=s><input type=submit value=“Search”>
</form>
search.cgi读取表单变量并将它剖析成字:
my $query = $ENV{'QUERY_STRING'};
$query =~ s/s=//;
$query =~ s/%[0-9a-fA-F]{2}//g;
my@words =($query =~ /\w+/g);
下面,它打开包含倒序索引的 DBM文件:
use DB_File;
dbmopen(%db,“search_index.db”,0);
我们执行查询的策略是为每个相关文件保留一个计数器。
my %counters;
my $word;
for $word (@words){
my $pages = $db{lc $word};
my $page;
for $page ($pages =~ /(-\d)+/g){
$counters{$page}++;}}
如果一个文件包含全部要查找的字,它的计数器在每次循环时都增加1,所以它的值将和要查找的字数相等。下面的脚本找出那些文件并显示出来:
for $page (sort keys %counters){
if($counters{$page}==scalar(@words)){
my $href = $db{$page};
print“$href<br>”;}}
这就可以了。当然,这个小搜索引擎还有不少需要改进的地方,但那只是编程的问题了。