2007年06月10日

Lucene 是一个基于 Java 的全文检索工具包,你可以利用它来为你的应用程序加入索引和检索功能。Lucene 目前是著名的 Apache Jakarta 家族中的一个开源项目,下面我们即将学习 Lucene 的索引机制以及它的索引文件的结构。

在这篇文章中,我们首先演示如何使用 Lucene 来索引文档,接着讨论如何提高索引的性能。最后我们来分析 Lucene 的索引文件结构。需要记住的是,Lucene 不是一个完整的应用程序,而是一个信息检索包,它方便你为你的应用程序添加索引和搜索功能。

架构概览

图一显示了 Lucene 的索引机制的架构。Lucene 使用各种解析器对各种不同类型的文档进行解析。比如对于 HTML 文档,HTML 解析器会做一些预处理的工作,比如过滤文档中的 HTML 标签等等。HTML 解析器的输出的是文本内容,接着 Lucene 的分词器(Analyzer)从文本内容中提取出索引项以及相关信息,比如索引项的出现频率。接着 Lucene 的分词器把这些信息写到索引文件中。

图一:Lucene 索引机制架构
图一:Lucene 索引机制架构


用Lucene索引文档

接下来我将一步一步的来演示如何利用 Lucene 为你的文档创建索引。只要你能将要索引的文件转化成文本格式,Lucene 就能为你的文档建立索引。比如,如果你想为 HTML 文档或者 PDF 文档建立索引,那么首先你就需要从这些文档中提取出文本信息,然后把文本信息交给 Lucene 建立索引。我们接下来的例子用来演示如何利用 Lucene 为后缀名为 txt 的文件建立索引。

1. 准备文本文件

首先把一些以 txt 为后缀名的文本文件放到一个目录中,比如在 Windows 平台上,你可以放到 C:\\files_to_index 下面。

2. 创建索引

清单1是为我们所准备的文档创建索引的代码。

清单1:用 Lucene 索引你的文档

package lucene.index;
            import java.io.File;
            import java.io.FileReader;
            import java.io.Reader;
            import java.util.Date;
            import org.apache.lucene.analysis.Analyzer;
            import org.apache.lucene.analysis.standard.StandardAnalyzer;
            import org.apache.lucene.document.Document;
            import org.apache.lucene.document.Field;
            import org.apache.lucene.index.IndexWriter;
            /**
            * This class demonstrates the process of creating an index with Lucene
            * for text files in a directory.
            */
            public class TextFileIndexer {
            public static void main(String[] args) throws Exception{
            //fileDir is the directory that contains the text files to be indexed
            File   fileDir  = new File("C:\\files_to_index ");
            //indexDir is the directory that hosts Lucene's index files
            File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            File[] textFiles  = fileDir.listFiles();
            long startTime = new Date().getTime();
            //Add documents to the index
            for(int i = 0; i < textFiles.length; i++){
            if(textFiles[i].isFile() >> textFiles[i].getName().endsWith(".txt")){
            System.out.println("File " + textFiles[i].getCanonicalPath()
            + " is being indexed");
            Reader textReader = new FileReader(textFiles[i]);
            Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Text("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            }
            }
            indexWriter.optimize();
            indexWriter.close();
            long endTime = new Date().getTime();
            System.out.println("It took " + (endTime - startTime)
            + " milliseconds to create an index for the files in the directory "
            + fileDir.getPath());
            }
            }
            

正如清单1所示,你可以利用 Lucene 非常方便的为文档创建索引。接下来我们分析一下清单1中的比较关键的代码,我们先从下面的一条语句开始看起。

Analyzer luceneAnalyzer = new StandardAnalyzer();
            

这条语句创建了类 StandardAnalyzer 的一个实例,这个类是用来从文本中提取出索引项的。它只是抽象类 Analyzer 的其中一个实现。Analyzer 也有一些其它的子类,比如 SimpleAnalyzer 等。

我们接着看另外一条语句:

IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            

这条语句创建了类 IndexWriter 的一个实例,该类也是 Lucene 索引机制里面的一个关键类。这个类能创建一个新的索引或者打开一个已存在的索引并为该所引添加文档。我们注意到该类的构造函数接受三个参数,第一个参数指定了存储索引文件的路径。第二个参数指定了在索引过程中使用什么样的分词器。最后一个参数是个布尔变量,如果值为真,那么就表示要创建一个新的索引,如果值为假,就表示打开一个已经存在的索引。

接下来的代码演示了如何添加一个文档到索引文件中。

Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Text("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            

首先第一行创建了类 Document 的一个实例,它由一个或者多个的域(Field)组成。你可以把这个类想象成代表了一个实际的文档,比如一个 HTML 页面,一个 PDF 文档,或者一个文本文件。而类 Document 中的域一般就是实际文档的一些属性。比如对于一个 HTML 页面,它的域可能包括标题,内容,URL 等。我们可以用不同类型的 Field 来控制文档的哪些内容应该索引,哪些内容应该存储。如果想获取更多的关于 Lucene 的域的信息,可以参考 Lucene 的帮助文档。代码的第二行和第三行为文档添加了两个域,每个域包含两个属性,分别是域的名字和域的内容。在我们的例子中两个域的名字分别是"content"和"path"。分别存储了我们需要索引的文本文件的内容和路径。最后一行把准备好的文档添加到了索引当中。

当我们把文档添加到索引中后,不要忘记关闭索引,这样才保证 Lucene 把添加的文档写回到硬盘上。下面的一句代码演示了如何关闭索引。

indexWriter.close();
            

利用清单1中的代码,你就可以成功的将文本文档添加到索引中去。接下来我们看看对索引进行的另外一种重要的操作,从索引中删除文档。


从索引中删除文档

类IndexReader负责从一个已经存在的索引中删除文档,如清单2所示。

清单2:从索引中删除文档

File   indexDir = new File("C:\\luceneIndex");
            IndexReader ir = IndexReader.open(indexDir);
            ir.delete(1);
            ir.delete(new Term("path","C:\\file_to_index\lucene.txt"));
            ir.close();
            

在清单2中,第二行用静态方法 IndexReader.open(indexDir) 初始化了类 IndexReader 的一个实例,这个方法的参数指定了索引的存储路径。类 IndexReader 提供了两种方法去删除一个文档,如程序中的第三行和第四行所示。第三行利用文档的编号来删除文档。每个文档都有一个系统自动生成的编号。第四行删除了路径为"C:\\file_to_index\lucene.txt"的文档。你可以通过指定文件路径来方便的删除一个文档。值得注意的是虽然利用上述代码删除文档使得该文档不能被检索到,但是并没有物理上删除该文档。Lucene 只是通过一个后缀名为 .delete 的文件来标记哪些文档已经被删除。既然没有物理上删除,我们可以方便的把这些标记为删除的文档恢复过来,如清单 3 所示,首先打开一个索引,然后调用方法 ir.undeleteAll() 来完成恢复工作。

清单3:恢复已删除文档

File   indexDir = new File("C:\\luceneIndex");
            IndexReader ir = IndexReader.open(indexDir);
            ir.undeleteAll();
            ir.close();
            

你现在也许想知道如何物理上删除索引中的文档,方法也非常简单。清单 4 演示了这个过程。

清单4:如何物理上删除文档

File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,false);
            indexWriter.optimize();
            indexWriter.close();
            

在清单 4 中,第三行创建了类 IndexWriter 的一个实例,并且打开了一个已经存在的索引。第 4 行对索引进行清理,清理过程中将把所有标记为删除的文档物理删除。

Lucene 没有直接提供方法对文档进行更新,如果你需要更新一个文档,那么你首先需要把这个文档从索引中删除,然后把新版本的文档加入到索引中去。


提高索引性能

利用 Lucene,在创建索引的工程中你可以充分利用机器的硬件资源来提高索引的效率。当你需要索引大量的文件时,你会注意到索引过程的瓶颈是在往磁盘上写索引文件的过程中。为了解决这个问题, Lucene 在内存中持有一块缓冲区。但我们如何控制 Lucene 的缓冲区呢?幸运的是,Lucene 的类 IndexWriter 提供了三个参数用来调整缓冲区的大小以及往磁盘上写索引文件的频率。

1.合并因子(mergeFactor)

这个参数决定了在 Lucene 的一个索引块中可以存放多少文档以及把磁盘上的索引块合并成一个大的索引块的频率。比如,如果合并因子的值是 10,那么当内存中的文档数达到 10 的时候所有的文档都必须写到磁盘上的一个新的索引块中。并且,如果磁盘上的索引块的隔数达到 10 的话,这 10 个索引块会被合并成一个新的索引块。这个参数的默认值是 10,如果需要索引的文档数非常多的话这个值将是非常不合适的。对批处理的索引来讲,为这个参数赋一个比较大的值会得到比较好的索引效果。

2.最小合并文档数

这个参数也会影响索引的性能。它决定了内存中的文档数至少达到多少才能将它们写回磁盘。这个参数的默认值是10,如果你有足够的内存,那么将这个值尽量设的比较大一些将会显著的提高索引性能。

3.最大合并文档数

这个参数决定了一个索引块中的最大的文档数。它的默认值是 Integer.MAX_VALUE,将这个参数设置为比较大的值可以提高索引效率和检索速度,由于该参数的默认值是整型的最大值,所以我们一般不需要改动这个参数。

清单 5 列出了这个三个参数用法,清单 5 和清单 1 非常相似,除了清单 5 中会设置刚才提到的三个参数。

清单5:提高索引性能

/**
            * This class demonstrates how to improve the indexing performance
            * by adjusting the parameters provided by IndexWriter.
            */
            public class AdvancedTextFileIndexer  {
            public static void main(String[] args) throws Exception{
            //fileDir is the directory that contains the text files to be indexed
            File   fileDir  = new File("C:\\files_to_index");
            //indexDir is the directory that hosts Lucene's index files
            File   indexDir = new File("C:\\luceneIndex");
            Analyzer luceneAnalyzer = new StandardAnalyzer();
            File[] textFiles  = fileDir.listFiles();
            long startTime = new Date().getTime();
            int mergeFactor = 10;
            int minMergeDocs = 10;
            int maxMergeDocs = Integer.MAX_VALUE;
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            indexWriter.mergeFactor = mergeFactor;
            indexWriter.minMergeDocs = minMergeDocs;
            indexWriter.maxMergeDocs = maxMergeDocs;
            //Add documents to the index
            for(int i = 0; i < textFiles.length; i++){
            if(textFiles[i].isFile() >> textFiles[i].getName().endsWith(".txt")){
            Reader textReader = new FileReader(textFiles[i]);
            Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Keyword("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
            }
            }
            indexWriter.optimize();
            indexWriter.close();
            long endTime = new Date().getTime();
            System.out.println("MergeFactor: " + indexWriter.mergeFactor);
            System.out.println("MinMergeDocs: " + indexWriter.minMergeDocs);
            System.out.println("MaxMergeDocs: " + indexWriter.maxMergeDocs);
            System.out.println("Document number: " + textFiles.length);
            System.out.println("Time consumed: " + (endTime - startTime) + " milliseconds");
            }
            }
            

通过这个例子,我们注意到在调整缓冲区的大小以及写磁盘的频率上面 Lucene 给我们提供了非常大的灵活性。现在我们来看一下代码中的关键语句。如下的代码首先创建了类 IndexWriter 的一个实例,然后对它的三个参数进行赋值。

int mergeFactor = 10;
            int minMergeDocs = 10;
            int maxMergeDocs = Integer.MAX_VALUE;
            IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
            indexWriter.mergeFactor = mergeFactor;
            indexWriter.minMergeDocs = minMergeDocs;
            indexWriter.maxMergeDocs = maxMergeDocs;
            

下面我们来看一下这三个参数取不同的值对索引时间的影响,注意参数值的不同和索引之间的关系。我们为这个实验准备了 10000 个测试文档。表 1 显示了测试结果。

表1:测试结果
表1:测试结果

通过表 1,你可以清楚地看到三个参数对索引时间的影响。在实践中,你会经常的改变合并因子和最小合并文档数的值来提高索引性能。只要你有足够大的内存,你可以为合并因子和最小合并文档数这两个参数赋尽量大的值以提高索引效率,另外我们一般无需更改最大合并文档数这个参数的值,因为系统已经默认将它设置成了最大。


Lucene 索引文件结构分析

在分析 Lucene 的索引文件结构之前,我们先要理解反向索引(Inverted index)这个概念,反向索引是一种以索引项为中心来组织文档的方式,每个索引项指向一个文档序列,这个序列中的文档都包含该索引项。相反,在正向索引中,文档占据了中心的位置,每个文档指向了一个它所包含的索引项的序列。你可以利用反向索引轻松的找到那些文档包含了特定的索引项。Lucene正是使用了反向索引作为其基本的索引结构。


索引文件的逻辑视图

在Lucene 中有索引块的概念,每个索引块包含了一定数目的文档。我们能够对单独的索引块进行检索。图 2 显示了 Lucene 索引结构的逻辑视图。索引块的个数由索引的文档的总数以及每个索引块所能包含的最大文档数来决定。

图2:索引文件的逻辑视图
图2:索引文件的逻辑视图


Lucene 中的关键索引文件

下面的部分将会分析Lucene中的主要的索引文件,可能分析有些索引文件的时候没有包含文件的所有的字段,但不会影响到对索引文件的理解。

1.索引块文件

这个文件包含了索引中的索引块信息,这个文件包含了每个索引块的名字以及大小等信息。表 2 显示了这个文件的结构信息。

表2:索引块文件结构
表2:索引块文件结构

2.域信息文件

我们知道,索引中的文档由一个或者多个域组成,这个文件包含了每个索引块中的域的信息。表 3 显示了这个文件的结构。

表3:域信息文件结构
表3:域信息文件结构

3.索引项信息文件

这是索引文件里面最核心的一个文件,它存储了所有的索引项的值以及相关信息,并且以索引项来排序。表 4 显示了这个文件的结构。

表4:索引项信息文件结构
表4:索引项信息文件结构

4.频率文件

这个文件包含了包含索引项的文档的列表,以及索引项在每个文档中出现的频率信息。如果Lucene在索引项信息文件中发现有索引项和搜索词相匹配。那么 Lucene 就会在频率文件中找有哪些文件包含了该索引项。表5显示了这个文件的一个大致的结构,并没有包含这个文件的所有字段。

表5:频率文件的结构
表5:频率文件的结构

5.位置文件

这个文件包含了索引项在每个文档中出现的位置信息,你可以利用这些信息来参与对索引结果的排序。表 6 显示了这个文件的结构

表6:位置文件的结构
表6:位置文件的结构

到目前为止我们介绍了 Lucene 中的主要的索引文件结构,希望能对你理解 Lucene 的物理的存储结构有所帮助。


总结

目前已经有非常多的知名的组织正在使用 Lucene,比如,Lucene 为 Eclipse 的帮助系统,麻省理工学院的 OpenCourseWare 提供了搜索功能。通过阅读这篇文章,希望你能对 Lucene 的索引机制有所了解,并且你会发现利用 Lucene 创建索引是非常简单的事情。


参考资料

学习

获得产品和技术

  • 下载 Lucene 最新版本。

讨论


关于作者

周登朋,上海交通大学研究生,目前在IBM上海国际化实验室(SGL)实习,对Java技术以及信息检索技术非常感兴趣,你可以通过 zhoudengpeng@yahoo.com.cn来联系他.

1、查询处理:百度怎么处理用户查询的呢?归纳如下:首先根据分割符号将查询分开,然后看看是否有重复的字符串,如果有,就抛弃多余的,只保留一个,接着判断是否有英文或者数字,如果有的话,把英文或者数字当作一个整体保留并把前后的中文切开.

2、中文分词
百度对于少于3个字符的串没有切分;百度应该采取了两套索引机制,一种是按照单词索引,一种是按照N-GRAM索引;判断一个分词系统好不好,关键看两点,一个是消除歧义能力;一个是词典未登录词的识别比如人名,地名,机构名等.那么百度用的是什么方法?我的判断是用双向最大匹配算法,在这种情况下,百度采取最短路径方法,也就是切分的片断越少越好,比如< 古巴,比,伦理>和< 古巴比伦,理>相比选择后者,< 北京,华,烟云>和< 北,京华烟云>相比选择后者.还有类似的一些例子,这样基本可以解释这些输出结果.如果正向反向分词不一致,而且最短路径也相同,那怎么办?百度的可能选择是这种情况下选择单字少的那组切分结果.

百度一直宣传自己在中文处理方面的优势,从上面看,分词算法并无特殊之处,消歧效果并不理想,即使百度采取比上述分词算法复杂些的算法也难以说成是优势,如果说百度有优势的话,唯一的优势就是那个很大的专用词典,这个专用词典登录了人名(比如大长今),称谓(比如老太太),部分地名(比如阿联酋等),估计百度采用学术界公布的比较新的命名实体识别算法从语料库里面不断识别出词典未登录词,逐渐扩充这个专门词典.如果这就是优势的话,那么这个优势能够保持多久就是个很明显的问题.

3、拼写检查错误提示:
  后台作业: 
  (1)前面的文章我们说过,百度分词使用的词典至少包含两个词典一个是普通词典,另外一个是专用词典(专名等),百度利用拼音标注程序依次扫描所有词典中的每个词条,然后标注拼音,如果是多音字则把多个音都标上,比如”长大”,会被标注为”zhang da/chang da”两个词条.
  (2)通过标注完的词条,建立同音词词典,比如上面的”长大”,会有两个词条: zhang daà长大” , chang daà长大.
  (3)利用用户查询LOG频率信息给予每个中文词条一个权重;
  (4)OK,同音词词典建立完成了,当然随着分词词典的逐步扩大,同音词词典也跟着同步扩大;

  拼写检查:
  (1)用户输入查询,如果是多个子字符串,不作拼写检查;
  (2)对于用户查询,先查分词词典,如果发现有这个单词词条,OK,不作拼写检查;
  (3)如果发现词典里面不包含用户查询,启动拼写检查系统;首先利用拼音标注程序对用户输入进行拼音标注;
  (4)对于标注好的拼音在同音词词典里面扫描,如果没有发现则不作任何提示;
  (5)如果发现有词条,则按照顺序输出权重比较大的几个提示结果;

  拼音提示:
  (1)对于用户输入的拼音在同音词词典里面扫描,如果没有发现则不作任何提示;
  (2)如果发现有词条,则按照顺序输出权重比较大的几个提示结果;

4、中文分词改进:上面说过,经过分析得出百度的分词系统采用双向最大匹配分词,但是后来发现推理过程中存在一个漏洞,而且推导出来的百度分词算法步骤还是过于繁琐,所以进一步进行分析,看看是否前面的推导有错误.

总结:
   重新归纳一下百度的分词系统:首先用专有词典采用最大正向匹配分词,切分出部分结果,剩余没有切分交给普通词典,同样采取正向最大匹配分词,最后输出结果.
   另外,GOOGLE也是采用正向最大匹配分词算法,不过好像没有那个专用词典,所以很多专名都被切碎了.

中科院软件所  张俊林

一.  介绍

 

统计结果表明,近似镜像网页数占总网页数的比例高达全部页面的29%,而完全相同的页面大约占全部页面的22%。这些重复网页有的是没有一点改动的拷贝,有的在内容上稍作修改,比如同一文章的不同版本,一个新一点,一个老一点,有的则仅仅是网页的格式不同(如 HTML, Postscript,文献[Models and Algorithms for Duplicate Document Detection 1999]将内容重复归结为以下四个类型:

 

1.如果2篇文档内容和格式上毫无差别,则这种重复叫做full-layout duplicate

 

2.如果2篇文档内容相同,但是格式不同,则叫做full-content duplicates

 

3.如果2篇文档有部分重要的内容相同,并且格式相同,则称为partial-layout duplicates

 

4.如果2篇文档有部分重要的内容相同,但是格式不同,则称为partial-content duplicates

 

近似重复网页发现技术就是通过技术手段快速全面发现这些重复信息的手段.如何快速准确地发现这些内容上相似的网页已经成为提高搜索引擎服务质量的关键技术之一。发现重复或者近似网页对于搜索引擎有很多好处:

 

1.              首先,如果我们能够找出这些重复网页并从数据库中去掉,就能够节省一部分存储空间,进而可以利用这部分空间来存放更多的有效网页内容,同时也提高了web检索的质量。

 

2.              其次,如果我们能够通过对以往搜集信息的分析,预先发现重复网页,在今后的网页搜集过程中就可以避开这些网页,从而提高有效网页的搜集速度。有研究表明重复网页随着时间级别不发生太大变化,所以这种从重复页面集合中选择部分页面进行索引是有效的.

3.              另外,如果某个网页的镜像度较高,也就预示着该网页相对重要,在搜集网页时应赋予它较高的优先级,而当搜索引擎系统在响应用户的检索请求并对输出结果排序时,应该赋予它较高的权值。

4.              从另外一个角度看,如果用户点击了一个死链接,那么可以将用户引导到一个相同页面,这样可以有效的增加用户的检索体验.因而近似镜像网页的及时发现有利于改善搜索引擎系统的服务质量。

 

 

二.  基本处理流程

 

通过分析现有技术,可以归纳出以下几个解决该问题的核心技术点,每个不同的技术基本上是由这几个技术点构成,无非是具体采纳的技术不同而已:

 

 

 

1.      文档对象的特征抽取:将文档内容分解,由若干组成文档的特征集合表示,这一步是为了方面后面的特征比较计算相似度.

 

2.      特征的压缩编码:通过HASH编码等文本向数字串映射方式以方便后续的特征存储以及特征比较.起到减少存储空间,加快比较速度的作用.

 

3.      文档相似度计算:根据文档特征重合比例来确定是否重复文档.

 

4.      聚类算法:通过叠代计算算出哪些文档集合是根据相似度计算是相近的;

 

5.       工程化问题:出于海量数据计算速度的考虑,提出一些速度优化算法以使得算法实用化.

 

 

  我们可以从几个不同的角度对于现有的方法进行分类:

 

l        按照利用的信息,现有方法可以分为以下三类

 

1.只是利用内容计算相似

 

2.结合内容和链接关系计算相似

 

3.结合内容,链接关系以及url文字进行相似计算

    评价:现有绝大部分方法还是利用文本内容进行相似识别,其它两种利用链接关系以及URL文字的方法还不是很成熟,而且从效果看引入其它特征收效并不明显,所以从实际出发还是选择利用内容进行相似计算的算法.

 

 

l        按照特征提取的粒度现有方法可以分为以下三类

 

1.      按照单词这个级别的粒度进行特征提取.

 

2.      按照SHINGLE这个级别的粒度进行特征提取.SHNGLE是若干个连续出现的单词,级别处于文档和单词之间,比文档粒度小,比单词粒度大.

 

3.      按照整个文档这个级别的粒度进行特征提取

 

   评价:

 

目前这个领域里面很多工作借鉴类似于信息检索的方法来识别相似文档,其本质和SHINGLE等是相同的,都是比较两个文档的重合程度,但是区别是SHINGLE是将若干单词组成片断,粒度比较大,而信息检索类方法其实是用单词作为比较粒度,粒度比较小,粒度越大计算速度越快,而粒度越小计算速度越慢,所以信息检索类方法是不实用的,而且对SHINGLE的改进以及新提出的方法的发展趋势也是粒度越来越大,这样才能解决实际使用中速度的问题。粒度最大的极端情况是每个文档用一个HASH函数编码(比如MD5),这样只要编码相同就说明文档完全相同,但是粒度太大带来的问题是对于细微的变化文档无法判别,只能判断是否完全相同,至于部分相同以及相同的程度无法判断.

 

    所以,现有方法也可以从以下角度分类:粒度。最小粒度:单词;中等粒度:SHINGLE;最大粒度:整个文档;可见SHINGLE类方法其实是在速度和精确程度上的一种折中方法。可以探讨不同粒度的效果,比如以句子为单位进行编码,以段落为单位编码等不同粒度的编码单位,还可以考虑动态的编码:首先以自然段落编码进行判别,如果发现部分相似,然后针对不同的部分再以细小粒度比如句子甚至单词级别的比较 所谓SUPER SHINGLE就是将粒度放大得到的。粒度越大,好处是计算速度越快(对于MD5整个文档来说,每个文档一个HASH编码,然后排序,将相同的找出,是速度最快的),缺点是会遗漏很多部分相似的文档;粒度越小,好处是招回率比较高,缺点是计算速度减慢。

 

 

 

l        按照去处重复的级别进行分类,去处重复三个级别:

 

1.      镜像站点:根据站点内相似页面多少进行判断.实现相对简单.

 

2.      完全相同网页:实现相对简单并且速度比较块,可以根据页面MD5整个文档来说,每个文档一个HASH编码,然后排序,将相同的找出.

 

3.      部分相同页面:实现相对负责,目前大多工作在这个部分.

 

 

 

评价:

 

三个级别应该从最高级别到较低级别分别进行,因为有很大比例(22%)的内容是完全相同的,这个部分实现起来相对简单,而且如果这个部分已经识别,那么针对部分相同页面的计算量会大量减少,这样应该可以减少总体的计算时间..

 

 

 

l        按照去重的时机,可以分为以下三类

 

(1)    抓取页面的时候去重,这样可以减少带宽以及减少存储数量;

 

(2)    索引之后进行去重;

 

(3)    用户检索时候进行再次去重;增加准确性,耗费时间;

 

 

 

 评价:

 

可以结合三个时机某个或者所有都结合,对于GOOGLE来说,很可能是结合了23两种方法, GOOGLE的很多思路建立在后台计算和实时计算联合,比如相关度计算,后台计算重要性得分,在用户输入查询后得到初始数据集合,然后根据这个数据集合之间文档的关系重新调整顺序;比如去处重复,首先在后台进行重复发现,为了增加精确度,在返回查询结果后,在返回文档集合内,又根据“描述”部分重新计算哪些文档是重复的,这样增加了准确性,估计其它很多相关算法也采取这种联合策略,为了加快速度,实时计算部分可以和CACHE部分结合进行计算。

 

 

 

l        按照不同的特征选择方法,有几种方式:

 

1.      完全保留特征

 

2.      特征选择,设置不同的选择策略来保留部分特征,抛弃其它特征

 

a.       比如对于单词级别的抛弃权重小的单词(I-MATCH)

 

b.      对于SHINGLE方法,可以保留部分SHINGLE抛弃其它SHINGLE

 

(1)   一种是保留FINGERPRINTI个位置为0SHINGLE,其它抛弃;

 

(2)   一种是每隔ISHINGLE进行抽样保留,其它抛弃;这两种得到的文档SHINGLE数目是变长的;

 

(3)   一种是选择最小的KSHINGLE,这种得到定长的SHINGLE数目;

 

(4)    84RABIN FINGERPRINT函数对于每个SHINGLE进行计算,保留数值最小的84FINGERPRINT,这个方法是定长的.

 

 

 

对于SHINGLE类方法来说,还可以区分为:定长的和变长的block切分算法

 

    定长算法:速度快,但是如果内容有稍微变化(比如插入或者删除一个字符或者单词),其影响会比较大。比如Shingle及其改进方法(Super-Shingle),CSC及其改进方法(CSC-SS)。

 

    变长算法:速度相对慢,但是内容变化只是造成局部影响。比如CDC,TTTD等算法。

 

 

 

评价: 为了提高计算速度,一种策略是在特征提取的时候,抛弃部分特征,保留部分特征,通过减少特征数目来加快计算速度.另外一个策略是粒度尽可能加大,比如SUPER-SHINGLE,MEGA-SHINGLE甚至是文档基本;为了提高算法效果,策略是采取变长的内容切割算法比如CSC算法等;这三种策略是方法加快速度和准确性的发展方向.

 

 

 

 

 

一些初步的结论:

 

1.       对于信息检索类型的方法来说,由于其特征选择是基于单词的,所以计算速度是个根本的问题,所以基本上是不实用的;

2.       从利用的信息来看,实用的系统还是应该立足于只是利用文本内容来判别相似性,排除掉利用链接信息等方法;

 

3.       从算法特征抽取粒度来看,应该立足于SHINLGE类的粒度甚至是文档级别的粒度算法;SHINGLE类别的算法又应该优先选择抛弃部分特征的算法以及变长的算法;

 

4.       从去重级别角度考虑,应该将完全相同的文档和部分相同的文档识别分开进行,而且首先进行完全相同文档的识别,这样会有效加快计算速度;

 

5.       从去重时机考虑,可以考虑结合后台去重以及实时去重,这样增加去重的效果;

 

6.       从压缩编码方法来看,最有效的方式可能是RABIN FINGERPRINT变体算法;

 

7.       从聚类方法来看,最有效的方式可能是UNION FIND算法,目前比较快的算法基本上都采用这个方法;

8.       从整体方法选择来看,应该选择改进的SHINLGE方法,在此基础上进行进一步的改进;

 

 

 

 

三.  方法效率比较

 

1.       SHINGLING 方法:时间效率O((mn)2) ,其中 mSHINGLE的大小,n是文档数目.计算时间为:3千万文档,10台机器算一天,或者一台机器算10;

2.      改进的SHINGLE方法(On the Evolution of Clusters of Near-Duplicate Web Pages.):时间效率接近于线性的O(n),计算时间为:1亿5千万网页计算3个小时;

 

3.      IMACH方法: 最坏的情况下时间复杂度是(O(d log d)),速度比较快

 

4.      BLOOM FILTER方法:10k数据花费大约66ms;

 

 

 

从计算效率考虑,速度排序为:

 

1.    改进的SHINGLE方法;

 

2.    IMATCH方法;

 

3.    BLOOM FILTER方法;

 

4.      SHINGLE方法;

 

 

 

 

 

四.  目前代表性解决方法分析

 

1.   Shingle方法(1997年)

 

a.      特征抽取

 

Shingle方法:所谓Shingle类似于自然语言处理中常用的N-GRAM方法,就是将相互连续出现窗口大小为N的单词串作为一个Shingle,两者的不同点在于Shingle是这些串的集合,相同的串会合并为一个,N-GRAM则由于考虑的是文本线性结构,所以没有相同合并步骤.每个Shingle就是文档的一个特征,一篇文档就是由所有这些Shingle构成的.

 

 

 

b.      压缩编码

 

40 bit长度 Rabin FingerPrint方法;至于存储方式则类似于传统信息检索领域的倒排文档技术,存储<Shingle,ID>信息以记录某个特征在哪些文档中出现过,然后进一步计算文档的相似性;

 

 

 

c.       文档相似度计算

 

(1)   相似度:任意两个文档AB,相似度指的是两者相同的Shingle数目占两者Shingle数目总和的比例;

 

(2)   包含度:指的是两者相同的Shingle数目占某篇文档Shingle数目的比例;

 

 

 

d.      优化措施:

 

(1)   分布计算然后合并;

 

(2)   抛弃超高频出现Shingle,分析发现这些Shingle是无意义的片断;

 

(3)   完全相同文档保留一份进行聚类;(文档是否完全相同根据压缩编码后数值是否相同判断)

 

(4)   Super Shingle:关于ShingleShingle,从更大结构上计算相似性以节省存储空间;

 

 

 

 

 

2.      Google可能采取的方法

 

a.      特征抽取

 

类似于Shingle方法,不同点在于:对于每个单词根据HASH函数决定属于哪个LIST,这样每个文档由若干个这样的LIST构成;

 

 

 

b.      压缩编码

 

FingerPrint方法;对于组成文档的LIST进行FingerPrint方法计算; 

 

 

 

c.       文档相似度计算

 

            编辑距离(Edit Distance):如果两个文档有任何一个FingerPrint相似就判断为内容接近.

 

 

 

d.      聚类方法

 

首先对<FingerPrint,Doc ID>按照Doc ID进行排序;然后采取Union Find聚类方法,聚类结果就是相似文档集合;

 

 

 

e.      优化措施

 

 

 

3.   HP实验室方法(2005年)

 

a.      特征抽取

 

基于内容的Chunk方法:变长而非定长的Chunk算法(TTTD算法);将一篇文档分解为若干个长度不同的Chunk,每个Chunk作为文本的一个特征.shingle方法相比这种变长Chunk方法能够增加系统招回率;

 

 

 

b.      压缩编码

 

128bit MD5 HASH方法;每篇文章压缩编码后由若干 <Chunk 长度, 定长HASH编码>二元组构成;

 

 

 

c.       文档相似度计算

 

(1)   构建所有文档和Chunk构成的二分图;

 

(2)   找到文档A包含的所有CHUNK,计算这些CHUNK还被哪些其它文档包含;

 

(3)   计算这些文档和A的相似性;

 

 

 

d.      聚类方法:Union Find 算法

 

e.       优化措施:Bipartite 划分,本质上是将大规模数据分成小规模数据进行识别然后再合并结果.相当于分布计算;

 

 

 

4.bloom filter(2005)

 

(1).特征抽取方法

 

   基于内容的语块(Content-defined chunking CDC):CDC将文档切分为变长的内容片断,切分边界由rabin fringerprint和预先制定的maker数值匹配来进行判断。

 

(2)编码(构造 bloom filter集合元素)

 

    对于切分的片断进行编码。bloom filter的编码方式如下:整个文档是由片断构成的,文档由长为m的二值数组表示。在将一个元素(内容片断)进行编码插入集合的时候,利用k个不同的hash函数进行编码,每个hash函数设置m个位置的某个位置为1。这种技术以前主要用来进行判断某个元素是否被集合包含。

 

3相似度计算方法

 

   bloom filter方法:对于两个已经编码的文档(两个长度为m的二值数组),通过bit逻辑运算AND计算,如果两者很多位置都同时为1,那么两个文档被认为是近似的。

 

4优势

 

     1.文档编码形式简洁,便于存储。

 

     2.由于计算相似性是BIT逻辑运算,所以速度快。

 

3.相对Shingling 方式来说便于判断文档包含关系。(某个文档包含另外一个短小的文档)

 

 

 

5内容+链接关系2003年)

 

1.特征抽取方法

 

       这个方法在抽取特征的时候同时考虑了文档的内容因素以及链接关系因素。

 

       内容因素:通过Random Projection技术将文档内容从高维空间映射到低维空间,并且由实数表示,如果两个文档映射后的数字越接近则表明两者内容越相似。

 

       链接因素:通过考虑类似于PAGERANK的连接关系,将某个网页的内容因素计算获得的分值通过链接传播到其他网页(传播关系见下列公式),多次叠代计算后得到每个页面的链接得分。

 

                 

 

 

 

 

 

 

2.相似度计算方法

 

       每个文档由二元组<RP,HM>构成,RP代表内容部分的数值,HM代表链接关系代表的数值。如果两个文档每个项之间的差值都小于指定值,则判断两个文档是相似的。

 

3.效果

 

  只采取内容精度达到90%,两者结合精度达到93%。从中看出,链接的作用并不明显。这可能跟这个方法的链接使用方法有关,因为通过链接计算的还是内容的情况。

 

 

 

6I-Match方法2002年)

 

1I-Match不依赖于完全的信息分析,而是使用数据集合的统计特征来抽取文档的主要特征,将非主要特征抛弃。输入一篇文档,根据词汇的IDF值过滤出一些关键特征,并且计算出这篇文档的唯一的Hash值,那些Hash值相同的文档就是重复的。

 

2)使用SHA1作为Hash函数,因为它的速度很快而且适用于任何长度。SHA-1生成一个20-byte 或者160-bit hash值并且使用一个安全的冲突消解算法,使得不同的标志串(token streams)生成相同的hash值的概率非常低。.<docid, hashvalue>元组插入树结构的时间复杂度是(O(d log d)),其他的如检索数据结构(hash表)需要(O(d))。对重复(duplicate)的识别是在将数据插入hash数组或是树结构中进行的,任何的hash值的冲突就表示检测到一个重复内容。

 

3)最坏的情况下时间复杂度是(O(d log d)),速度比较快。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1. 什么是PageRank
2. PageRank的决定因素
3. 如何查知PageRank
4. PageRank的重要性
5. Google的前1,000项搜索结果
6. PageRank与其它影响网站排名因素间的区别
一:什么是PageRank(网页级别)
PageRank(网页级别)是Google用于评测一个网页“重要性”的一种方法。在揉合了诸如Title标识和Keywords标识等所有其它因素之后,Google通过PageRank来调整结果,使那些更具“重要性”的网页在搜索结果中另网站排名获得提升,从而提高搜索结果的相关性和质量。

  简单说来,Google通过下述几个步骤来实现网页在其搜索结果页(SERPS)中的排名:
  1) 找到所有与搜索关键词匹配的网页
  2) 根据页面因素如标题\关键词密度等排列等级
  3) 计算导入链接的锚文本中的关键词
  4) 通过PageRank得分调整网站排名结果
  事实上,真正的网站排名过程并不是这么简单,我们会在后面进行详细深入的阐述。

二:PageRank的决定因素
Google的PageRank是基于这样一个理论:若B网页设置有连接A网页的链接(B为A的导入链接时),说明B认为A有链接价值,是一个“重要”的网页。当B网页级别(重要性)比较高时,则A网页可从B网页这个导入链接分得一定的级别(重要性),并平均分配给A网页上的导出链接。

导入链接(也叫逆向链接)指链至你网站的站点,也就是我们一般所说的“外部链接”。而当你链至另外一个站点,那么这个站点就是你的“导出链接”,即你向其它网站提供的本站链接。

PageRank反映了一个网页的导入链接的级别(重要性)。所以一般说来,PageRank是由一个网站的导入链接的数量和这些链接的级别(重要性)所决定的。

三:如何知道一个网页的PageRank得分
可从http://toolbar.google.com上下载并安装Google的工具栏,这样就能显示所浏览网页的PageRank得分了。PageRank得分从0到10,若不能显示PageRank得分,可检查所安装版本号,需将老版本完全卸载,重启机器后安装最新版本即可。

四:PageRank的重要性
搜索引擎网站排名算法中的各排名因子的重要性均取决于它们所提供信息的质量。但如果排名因子具有易操纵性,则往往会被一些网站管理员利用来实现不良竞争。例如初引入的排名因子之一–关键词元标识(Meta Keywords),是由于理论上它可以很好地概括反映一个页面的内容,但后来却由于一些网站管理员的恶意操纵而不得不黯然退出。所以“加权值”–即我们对该因子提供信息的信任程度是由排名因子的易操纵程度和操纵程度共同决定的。

PageRank无疑是颇难被操纵的一个排名因子了。但在它最初推出时针对的只是链接的数量,所以被一些网站管理员钻了空子,利用链接工厂和访客簿等大量低劣外部链接轻而易举地达到了自己的目的。Google意识到这个问题后,便在系统中整合了对链接的质量分析,并对发现的作弊网站进行封杀,从而不但有效地打击了这种做法,而且保证了结果的相关性和精准度。

五:Google的前1,000项搜索结果
一般说来,网站排名因素包括网页标题(META TITLE),网页正文中的关键词密度,锚文本(也叫链接文本,指链接或超链的文本内容)和PageRank所决定的。

请记住:单靠PageRank是无法使你获得比较理想的网站排名的。PageRank只是网站排名算法中的一个乘积因子,若你网站的其它排名因子的得分是零,就算你的PageRank是两百亿,最后的得分还是零。但这并不是说PageRank就毫无价值,而是在什么情况下PageRank才能完全发挥其功力。

如果在Google上进行广泛搜索,看起来好象有几千个结果,但实际显示最多前1,000项结果。例如对“car rental”,显示搜索结果为5,110,000,但实际显示结果只有826个。而且用时只有0.81秒。试想一下,0.84秒的时间就可以计算这五百万搜索结果的每个排名因子得分,然后给出最终我们所看到的网站排名结果吗?
答案就在于:搜索引擎选取与查询条件最相关的那些网页形成一个子集来加速搜索的速度。例如:假设子集中包含2,000个元素,搜索引擎所做的就是使用排名因子中的两到三个因素对整个数据库进行查询,找到针对这两三个排名因子得分较高的前2,000个网页。(请记住,虽然可能有五百多万搜索结果,但最终实际显示的1,000项搜索结果却是从这个2,000页的子集中提炼出来的。) 然后搜索引擎再把所有排名因子整合进这2,000项搜索结果组成的子集中并进行相应的网站排名。由于按相性进行排序,子集中越靠后的搜索结果(不是指网页)相关性(质量)也就越低,所以搜索引擎只向用户显示与查询条件最相关的前1,000项搜索结果。

请注意,在搜索引擎生成这2,000项网页的子集中我们强调了“相关性”这个词。即搜索引擎找寻的是与查询条件有共同主题的网页。如果这时候我们把PageRank考虑进去,就很可能得到一些PageRank很高但主题只是略微相关的一些搜索结果。显然这有违搜索引擎为用户提供最为相关和精准的搜索结果的原则。

一旦理解了为什么会如此,就说明了为什么你应当首先努力在“页面”因子和锚文本上下足工夫,最后才是PageRank。所以关键在于:

你必须首先在页面因素和/或锚文本上下足工夫,使这些排名因子能够获得足够的得分,从而使你的网站能够按目标关键词跻身于这2,000项搜索结果的子集中,否则PageRank再高也与事无补。

六:PageRank和其它排名因子之间的不同

网页Title标识 仅能被列出一次。
正文中的关键词 连续的重复只会降低关键词的重要性,重要的是接近度。
锚文本 加权值极高,但存在上限,超过上限的锚文本信息将被忽略或降低权值。
PageRank 潜质无穷,没有上限的限制,但需要大量工作。
备注 其它排名因子都存在一个上限(阙值),超过上限部分其权值将降低或不再计分。PageRank则不存在此问题。

Google–PageRank(网页级别)技术解密(二)

7. 非PageRank因素阙值
8. 使用阙值推知两种排名策略的价值
9. PageRank的计算

七:非PageRank因子的上限阙值(Non-PageRank Factor Threshold)
除了PageRank外,其它排名因子都存在一个阙值,也叫临界值或差值。即当增长到一定值时,因子的重要性反而开始慢慢降低,则该值就是非PageRank因子的阙值。

设阙值为1,000,如果网页A和B是我们对某一查询条件的其中两个查询结果,且A的总分数(包括页面因子得分和PageRank得分)是900,B是500,则显然A会排在B的前面。但由于A和B的分数均低于我们上面假设的非PageRank因子阙值,因而在不改变PageRank的情况下,我们可以通过对B页进行精心的页面优化使页面因子分数得到提高来使其排名超过A。但如果A的总得分升至1,100分,则B若还只是一味优化页面因子是远远不够的。在这种情况下,提升PageRank就成为首要任务了。

一般说来,Google的查询结果页中既可能包含一些分数超过阙值的网页,也可能包含一些分数低于阙值的网页。所以:

为了提高竞争能力,必须在阙值范围内尽可能提高页面的搜索引擎排名得分,否则会降低页面的竞争力。“页面因子”是接近和达到阙值最迅捷的方式,它与PageRank的结合使用才是提升网站排名得分的最佳优化策略。

八:使用阙值推知两种排名策略的价值
阙值解释了搜索引擎商所遵循的原则和不同的实施途径,同时亦阐述了为什么会产生关于PageRank的一些误解。我们可以把这两种策略当成两个人A和B。

A认为“PageRank”并不重要。他们已有数年网页优化经验并知道如何完美地利用“页面因素”来达到优化的目的。他们亦理解基本的锚文本,但对PageRank得分毫不在意。结果如何呢?由于最大化地使用了“页面因子”,从而使A迅速达到“非PageRank因子的阙值”。所以通过精心选择关键词可使他们获得较好的网站排名。而且只要网站内容比较好,随着时间推移总会有排名高的站点链接,涓涓细流汇成河。A最后亦得到了PageRank得分,并籍此巩固了排名。

B认为“PageRank”十分重要。他掌握了很多关于提升PageRank得分的信息,并为提高该得分下足了工夫。结果又如何呢?B的做法和A相反,但A在非PageRank因子上下工夫,结果却得到了PageRank得分。而B在PageRank因子上下工夫,结果却得到非PageRank因子得分。究其原因,就是由于提高PageRank得分需要外部链接,链接又具有锚文本,从而通过精心挑选外部链接的锚文本,B自发提高了其非PageRank因子的得分,从而赢得了较高的PageRank得分。

虽然这只是两个极端,但我们可以利用它们来推知这两种途径各自的优缺点:

对象
优点
缺点
A:忽略PageRank
  • 网站排名在短期内就可得到提升
  • 自我生成链接节省了工作量
  • 需投入大量工作维持网站排名
  • 对新竞争者的应变速度较慢
  • B:忽略页面排名因子
  • 可获得可靠网站排名,并可在需要时轻松修改页面因素使排名迅速提升
  • 极可能从非搜索类引擎来源上获得更高访问量
  • 网站排名提升较慢
  • 操作难度较大
  • 容易为SPAM过滤程序所制
  • 对象 优点 缺点
    A:忽略PageRank 网站排名在短期内就可得到提升

    自我生成链接节省了工作量
    需投入大量工作维持网站排名

    对新竞争者的应变速度较慢

    B:忽略页面排名因子 可获得可靠网站排名,并可在需要时轻松修改页面因素使排名迅速提升

    极可能从非搜索类引擎来源上获得更高访问量
    网站排名提升较慢

    操作难度较大

    容易为SPAM过滤程序所制

    事实上,我们前面说过,最终排名得分=所有非PageRank因子实际得分x实际PageRank得分。亦即二者相辅相成,再加上随着网上营销方式的发展壮大,关键词的竞争也变的愈来愈激烈,这种情况下只靠非PageRank因子得到好排名显然是不可能的。而且非PageRank因子存在着阙值的局限性。同时,对于竞争性极高的关键词,还存在着PageRank下限的问题。也就是说,除非网站的PageRank得分超过这个下限标准,否则网站排名很难上去。PageRank的下限由关键词的竞争度所决定。竞争性一般的关键词PageRank下限也不高,而对竞争较为激烈的关键词来说,它所要求的PageRank下限相应就要高。而PageRank得分的提升又非常有难,这时候非PageRank因子就变的非常重要了。
    综上所述:我们需要充分发挥各排名因子的优势来赢取理想的综合排名得分。同时关键词(竞争度适宜)的精心选择亦变的非常重要,它可以节省大量的支出。

    九:PageRank的计算方法
      PageRank (A) = (1-d) + d(PageRank (T1)/C(T1) + … + PageRank (Tn)/C(Tn))
      其中PageRank (A)表示给定页面A的PageRank得分;
      D为阻尼因子,一般设为0.85;
      PageRank (T1)表示一个指向A页的网站其本身的PageRank得分;
      C(T1)表示该页面所拥有的导出链接数量;
      PageRank (Tn)/C(Tn)表示为每一个指向A页的页面重复相同的操作步骤。

     

    事实上,计算某个页面的PageRank得分需要大量繁复计算。例如若计算A页的PageRank得分则首先要知道所有链至A页的网页(导入链接)的PageRank得分。要想知道这些外部链接页的PageRank得分,又需要先知道这些页面的外部链接的PageRank得分,等等。我们只需要知道:

    A页的外部链接B能够带给A的PageRank得分与B的导出链接数量成反比,即随着B上导出链接数的增加,带给A的PageRank得分亦随之降低。这同样表明了一个网页的PageRank得分是该网页对其它页面投票的一个基本的度量形式。一个网页可以投票给一个或多个导出链接,但其总投票权一定,并被平均分配给所有的导出链接。假设B的PageRank得分是5,且B上只有一条指向A的链接,那么A将获得B全部的PageRank得分(B没有损失任何东西,而A赢得了B的PageRank得分)。但如果B上有N个链接,则A只能得到B的PageRank得分的N分之一。

    我们可以用图表来阐述其工作原理。假设有四个网页A,B,C和D,它们相互链接,如表-1所示:

    表-1:链接前的PageRank得分

    表-2:链接后的PageRank得分

    假设这四个网页初始PageRank得分均为0。则根据上面的公式它们的PageRank得分都是0.15。我们计算一下链接后各自的PageRank得分情况。
    1.A链向B、C和D。A的初始PageRank得分是0.15,所以A的导出链接获得PageRank得分总数为 0.85 * 0.15 = 0.1275。B,C和D三个网页各得0.0425分。
    2.B链向C、B的初始PageRank得分也是0.15,所以其唯一链接页面C所能够获得的PageRank得分是0.85 * 0.15 = 0.1275分。
      C链向A,其0.1275的PageRank得分传递给唯一链接对象A。
      D链向C,其0.1275的PageRank得分传递给了C。
      现在各网页的PageRank得分结果如下:
      A:0.15 + 0.1275 (得自C) = 0.2775
      B:0.15 + 0.0425 (得自A) = 0.1925
      C:0.15 + 0.0425 (得自A) + 0.1275 (得自B) + 0.1275 (得自D) = 0.4475
      D:0.15 + 0.0425 (得自A) = 0.1925(如表-2所示)
    继续这样的计算,直到每个页面的数值逼近一个定值(PageRank属收敛函数)。最后可以发现,C的PageRank最高。而且外部链接的数量显著地改变了PageRank得的分布。

    表-3:最后的PageRank得分结果

    表-4:外部链接与PageRank得分对照表:

     
    导入链接
    数量
    导出链接
    数量
    最终PageRank得分
    C
    A/B/D
    3
    A
    1
    1.4860614724
    A
    C
    1
    B/C/D
    3
    1.4131522515
    B
    A
    1
    C
    1 0.5503931379
    D
    A
    1
    C
    1 0.5503931379

    十:PageRank的反馈性[出自chinaseo]
      
    PageRank的反馈机制说明了为什么一个网站的导出链接能够使网站自身受益。
    假设A页链向B,根据PageRank计算公式,其初始PageRank为0.15,链接后的PageRank变为1。如果A链向C,而C又链回A,则A此时的PageRank变成了1.4594594595。也就是说若A链向一个外部网页,而那个网页又回链向A的话,则使A的PageRank会增加。(如果A链向一个网页,而该网页又链向C,C再链向A的话,也会发生同样的情形)。如果把所有相互链接的页面看做是一个系统整体的话,其实链接前后系统总的PageRank并没有发生改变,只是由于不同链接关系的发生导致了PageRank对系统内每个链接页面的重新分布。

    表-6-1:无链接交换时:A到E页都是已被Google收录的页面。其中A和B是我们网站的内页

    表-7 未提供导出链接前 提供导出链接后
    A的PageRank得分: 1 1.3599321536
    B的PageRank得分: 1 0.7279711653
    网站的PageRank总分: 2 2.0879033189

    其增量比较小,整体则视情况而定。但有一点是显而易见的 – 提供导出链接的网页往往会通过一种叫做PageRank反馈的机制提升了自身的PageRank。

    结论:
    这表明和一些大型站点进行互惠链接交换是比较明智的。这些大网站均采用链接结构,并对链接页给予高度关注。你所链接的对象站点所包含的网页数量及其设计结构对于你网站的PageRank反馈总数有着显著的影响。

    十一:如何控制PageRank
    虽然PageRank因子很难控制,但我们可以通过其它技术来得到理想的结果。而且,良好优化过的页面因子整合较高的PageRank得分无疑会使网站更具竞争力。

    PageRank因子的优化可从下面三个方面着手:
      1. 导入链接。包括如何选取导入链接,获得导入链接所付出的努力是与收获呈正比的。
      2. 导出链接。包括导出链接的选取及它们在你网站上的合适位置,应使PageRank得到最大回馈(Feedback) 和最小损耗(Leakage)。
      3. 网站内部导航结构和内部页面的联接。实现PageRank在网站内部的良好分布。

    十二:导入链接(Links to Your Site)
    寻找导入链接时,一般总是容易陷入这样的误区:只看链接页的PageRank得分,得分越高就越好。而事实上,一个链接页的PageRank得分遵循平均分配原则被平均分配给该页面上的所有链接。所以,只注重外部链接的PageRank得分的链接策略无疑是片面的。正确的做法应该是既要考虑链接页的PageRank,又要考虑该页的链接数量(应注意:PageRank的单位是网页而不是网站,即每个页面都有其特定的PageRank。所以在寻找链接时应查看“链接”页面的PageRank,也就是说,需要考虑的是放置你网站链接的那个页面的PageRank得分情况。) 而且PageRank较高的站点对链接请求一般总是比较挑剔的。

    结论:
      那些看起来较为适宜,具有良好质量的网站都是理想的链接对象。先别去管它们的PageRank到底是多少,倘若它们既与你的网站相关,又具有较高的质量,那么总是会有益你的PageRank,只是个时间问题罢了。另外,网站被DMOZ和Yahoo收录亦能相当有效地提升PageRank。

    十三:导出链接(Links out of your site)
    导出链接并不会损失PageRank,但网站整体的PageRank将会降低。所以,选择导出链接时宜遵循这样的定律:
      1. 尽量保持自己网站的PageRank
      2. 尽量使内部页面分得尽可能多的PageRank

    向大家推荐一种方法:可以在网站上设立一个对导出链接的“评审”页。用于放置对外部链接站点的评审内容。每条评审内容应包括指向其相应外部站点的超链。(注意:由于搜索引擎的SPIDERS无法支持JAVASCRIPT,所以不宜用JAVA程序打开这些页面。)

    “评审”页应链回网站内部等级较高的一个页面 (最好是主页,其它重量级页也可)。这样做可显著降低网站PageRank的流失。放置外部链接的页面亦需链回主页及其它重要内部页面。但“评审”页上只要放置一个重量级内部页面即可(最好是主页)。此外,可以告诉你的”评审“链接对象你已经”评审“过他们的网站,这样一来他们很有可能会把你的这个”评审“页链接到他们自己的网站上,这样就可以从他们那里得到两个导入链接。自然效果就更好了。用文字描述太麻烦了,我们还是用图表来说明吧。(下表包括主页A,外部链接页页D和其它两个内页B和C)

    如果进行相同的计算,但包括review pages,则结果如下:
    表-9:加“评审”页后的各网页PageRank得分情况

    如果只看A,B,C和D页,则结果如下:

    无评审(Review)页 有评审(Review)页
    主页的PageRank: 0.9536152797 2.439718935
    B/C/D页的PageRank: 0.4201909959
    0.4201909959
    0.4201909959
    0.8412536982
    0.8412536982
    0.8412536982
    PageRank总计: 2.2141882674 4.9634800296

    在放置导出链接的页面上同时放一些网站的内部链接是提高PageRank的相当重要的内部因素之一。这种收益虽然无法和网站所从导入链接上获得的收益相提并论,但却极易操作,并可有益于网站读者。

    十四:网站的内部结构和联接

    一:网站的内部页面
    说完了“外部链接”,现在让我们来看看“内部链接”。如果PageRank确由页面投票的质量和数量所决定,那么我们立即就可以得出网站内链与PageRank的关系的一个重要结论:

    网站上每个已被Google收录的内部网页(内链)都是对该网站的一记投票,不过投票份量很小。因而,一个网站若能拥有更多已被Google收录的内部网页,就有可能获得更多的总投票。

    这样一来,我们可以通过创建大量内部网页来提高网站整体的PageRank。但这还远远不够。因为我们此处所指的内部网页是指已被Google收录,即拥有自己的PageRank的那些内链页面。这些网页之所以被Google检索是由于它们具备丰富充实的内容。所以应尽力充实和丰富你的网站,一旦网站内容得到充实和丰富,会有更多的内页得到检索,从而带来更多的PageRank。同时“升值”的网站也会获得更多站点的青睐,从而会有更多的站点主动链接你。

    简言之,就提升PageRank而言,对“内”最需要做的就是为网站填充更加丰富和有价值的内容。应确保网页内容不会过长或过短,如有必要可将网页内容分割成若干网页。

    二:网站的内部结构&联接
    网站有三种内部联方式,宜结合使用这三种联接方式进行网站的建设。假设一个网站由“主页”,“关于我们”页,“产品介绍”页和“更多信息”页这四个页面组成,通过下表我们可以看出每种结构对网站PageRank的影响度。

    表-11:层级结构(Hierarchical)

    表-12:环路网站结构(Looping)

    表-13:内页广泛互联的网站结构(Extensive Interlinking)

    表14-16是内部页面在不同结构网站上的PageRank分布情况。了解了这一点我们就可以从“内部链接”着手来获得最大的PageRank反馈。

     

    表-14:层级结构的PageRank分布(总PageRank=4)

    表-15: 环路结构的PageRank分布(总PageRank=4)

    表-16:内页广泛互联结构的PageRank分布(总PageRank=4)

    在未考虑外部链接因素的前提下,可以看出类层级结构(Hierarchical-Like)能够改变网站内部页面的PageRank分布。(注意:我们所指的并不一定是严格意义上的层级结构,不过必须比环路结构(Looping)或广泛互联(Extensive Interlinking)结构包含更多层级结构的属性。)

    若PageRank均匀分布于网站内部页中,那么网站管理员可以通过层级结构这种能够使PageRank发生转移的性能来有选择地转移内部网页的PageRank:即把一些不太重要的页面的PageRank适当地转移到那些关键词竞争性比较强的网页上,或想提高特定关键词排名的网页上去,从而使网站获得最大的收益。

    上面只是封闭网站设计结构(即无导入也无导出链接的结构)下内部各网页的分布情况,如果加入外部链接–即导入和导出链接后情况又会怎样呢?如表17-19所示:

    表-17:非封闭层级网站结构的PageRank分布

    表-18:非封闭环路网站结构的PageRank分布

    表-19:非封闭内页广泛互联网站结构的PageRank分布

      无导入/导出链接PageRank分布 有无导入/导出链接PageRank分布
    网站结构 主页的PR 其它各页的PR PR总数 主页PR 其它各页PR PR总数
    层级 1.9189189189 0.6936936937
    0.6936936937
    0.6936936937
    4 1.49842 0.57455
    0.57455
    0.57455
    3.222094
    环路 1 1
    1
    1
    4 0.68369 0.73113
    0.47787
    0.77146
    2.664173
    广泛互联 1 1
    1
    1
    4 0.95411 0.85476
    0.85476
    0.90469
    3.568342
    结论: 内页广泛互联(Extensive Interlinking)的网站结构(表13)可以最大程度地保留一个网站内部的PageRank,其次是层级(Hierarchical)结构(表11),最后才是环路(Looping)的网站结构(表12)。
    注意: 理论上是这样,但实际上如果让上万个内部网页互联起来又不太可能。所以还必须为网站各分枝选择合适的结构。不过对层级结构来说,越是增加子页数量效果越好。这是由于在导出链接页上增加了更多内部链接,按照PageRank平均分配给所有内/外链接的原则,受益最多的还是你。不过,如果增加太多内页,也会明显影响主页的PageRank。

    十五:Google如是说
    关于PageRank,最权威的发言人自然还是Google。虽然Google不会也不可能提供相关的技术信息,但我们亦可从中窥得一斑
    Chris:PageRank的命名是基于“Page”,还是和某个创始人有关?
    Google:PageRank是以Google的联合创始人兼总裁Larry Page的名字命名的。
    Chris:Google是否把PageRank视做显著区别于其它搜索引擎的一个特性?
    Google:PageRank是一种能够使Google在搜索速度和搜索结果的相关性上区别于其它搜索引擎的技术。不唯如此,在排名公式中Google还使用了100种其它的算法。
    Chris:Google是否认为引入PageRank可以显著提高搜索结果的质量?以后是否仍将继续使用PageRank?
    Google:由于PageRank使用了量化方法来分析链接,所以它仍将是决定Google搜索结果页排名的一个重要因素。
    Chris:您认为Google工具栏上的PageRank的信息对普通用户/网站管理员/搜索引擎优化专家来说各有什么意义?
    Google:Google工具栏上所提供的PageRank信息仅作为一种网站评估信息使用。用户们会觉得它很有趣,网站管理员一般用它来衡量网站性能。不过,由于PageRank只是一个大体评估,所以对搜索引擎专家的价值并不大。
    Chris:常有网站试图通过“链接工厂”和访客簿的手段达到提升PageRank的目的。对这样的网站Google有什么举措?
    Google:Google的工程师会经常更新Google的排名算法以防止对Google排名的恶意操纵。

    结束语:
    选择导入链接时应首先考虑对方网站的内容如何,然后再考察其导出链接的数量进行决策。而在建立本站的导出链接时则应尽量使自己网站的PageRank维持在最大回馈和最小流失上。

    应确保合理的网站设计结构和内部联接方式。网站的结构和内部联接方式也会对PageRank产生影响,可利用其特性有效进行PagaRank在网站内部页面的再分布及尽可能保持网站整体的PageRank。

    网站的PageRank的提升应与该网站的访问者体验息息相关。即使获得再高的PageRank,如果没有客户访问,一样毫无价值。所以网站的内容始终是提升PageRank最关键的因素之一

    在本篇文章中,你会学习到如何利用 Lucene 实现高级搜索功能以及如何利用 Lucene 来创建 Web 搜索应用程序。通过这些学习,你就可以利用 Lucene 来创建自己的搜索应用程序。

    架构概览

    通常一个 Web 搜索引擎的架构分为前端和后端两部分,就像图一中所示。在前端流程中,用户在搜索引擎提供的界面中输入要搜索的关键词,这里提到的用户界面一般是一个带有输入框的 Web 页面,然后应用程序将搜索的关键词解析成搜索引擎可以理解的形式,并在索引文件上进行搜索操作。在排序后,搜索引擎返回搜索结果给用户。在后端流程中,网络爬虫或者机器人从因特网上获取 Web 页面,然后索引子系统解析这些 Web 页面并存入索引文件中。如果你想利用 Lucene 来创建一个 Web 搜索应用程序,那么它的架构也和上面所描述的类似,就如图一中所示。

    Figure 1. Web 搜索引擎架构
    Web搜索引擎架构

    利用 Lucene 实现高级搜索

    Lucene 支持多种形式的高级搜索,我们在这一部分中会进行探讨,然后我会使用 Lucene 的 API 来演示如何实现这些高级搜索功能。

    布尔操作符

    大多数的搜索引擎都会提供布尔操作符让用户可以组合查询,典型的布尔操作符有 AND, OR, NOT。Lucene 支持 5 种布尔操作符,分别是 AND, OR, NOT, 加(+), 减(-)。接下来我会讲述每个操作符的用法。

    • OR: 如果你要搜索含有字符 A 或者 B 的文档,那么就需要使用 OR 操作符。需要记住的是,如果你只是简单的用空格将两个关键词分割开,其实在搜索的时候搜索引擎会自动在两个关键词之间加上 OR 操作符。例如,“Java OR Lucene” 和 “Java Lucene” 都是搜索含有 Java 或者含有 Lucene 的文档。
    • AND: 如果你需要搜索包含一个以上关键词的文档,那么就需要使用 AND 操作符。例如,“Java AND Lucene” 返回所有既包含 Java 又包含 Lucene 的文档。
    • NOT: Not 操作符使得包含紧跟在 NOT 后面的关键词的文档不会被返回。例如,如果你想搜索所有含有 Java 但不含有 Lucene 的文档,你可以使用查询语句 “Java NOT Lucene”。但是你不能只对一个搜索词使用这个操作符,比如,查询语句 “NOT Java” 不会返回任何结果。
    • 加号(+): 这个操作符的作用和 AND 差不多,但它只对紧跟着它的一个搜索词起作用。例如,如果你想搜索一定包含 Java,但不一定包含 Lucene 的文档,就可以使用查询语句“+Java Lucene”。
    • 减号(-): 这个操作符的功能和 NOT 一样,查询语句 “Java -Lucene” 返回所有包含 Java 但不包含 Lucene 的文档。

    接下来我们看一下如何利用 Lucene 提供的 API 来实现布尔查询。清单1 显示了如果利用布尔操作符进行查询的过程。

    清单1:使用布尔操作符

      //Test boolean operator
                public void testOperator(String indexDirectory) throws Exception{
                Directory dir = FSDirectory.getDirectory(indexDirectory,false);
                IndexSearcher indexSearcher = new IndexSearcher(dir);
                String[] searchWords = {"Java AND Lucene", "Java NOT Lucene", "Java OR Lucene",
                "+Java +Lucene", "+Java -Lucene"};
                Analyzer language = new StandardAnalyzer();
                Query query;
                for(int i = 0; i < searchWords.length; i++){
                query = QueryParser.parse(searchWords[i], "title", language);
                Hits results = indexSearcher.search(query);
                System.out.println(results.length() + "search results for query " + searchWords[i]);
                }
                }
                

    域搜索(Field Search)

    Lucene 支持域搜索,你可以指定一次查询是在哪些域(Field)上进行。例如,如果索引的文档包含两个域,TitleContent,你就可以使用查询 “Title: Lucene AND Content: Java” 来返回所有在 Title 域上包含 Lucene 并且在 Content 域上包含 Java 的文档。清单 2 显示了如何利用 Lucene 的 API 来实现域搜索。

    清单2:实现域搜索

    //Test field search
                public void testFieldSearch(String indexDirectory) throws Exception{
                Directory dir = FSDirectory.getDirectory(indexDirectory,false);
                IndexSearcher indexSearcher = new IndexSearcher(dir);
                String searchWords = "title:Lucene AND content:Java";
                Analyzer language = new StandardAnalyzer();
                Query query = QueryParser.parse(searchWords, "title", language);
                Hits results = indexSearcher.search(query);
                System.out.println(results.length() + "search results for query " + searchWords);
                }
                

    通配符搜索(Wildcard Search)

    Lucene 支持两种通配符:问号(?)和星号(*)。你可以使用问号(?)来进行单字符的通配符查询,或者利用星号(*)进行多字符的通配符查询。例如,如果你想搜索 tiny 或者 tony,你就可以使用查询语句 “t?ny”;如果你想查询 Teach, Teacher 和 Teaching,你就可以使用查询语句 “Teach*”。清单3 显示了通配符查询的过程。

    清单3:进行通配符查询

    //Test wildcard search
                public void testWildcardSearch(String indexDirectory)throws Exception{
                Directory dir = FSDirectory.getDirectory(indexDirectory,false);
                IndexSearcher indexSearcher = new IndexSearcher(dir);
                String[] searchWords = {"tex*", "tex?", "?ex*"};
                Query query;
                for(int i = 0; i < searchWords.length; i++){
                query = new WildcardQuery(new Term("title",searchWords[i]));
                Hits results = indexSearcher.search(query);
                System.out.println(results.length() + "search results for query " + searchWords[i]);
                }
                }
                

    模糊查询

    Lucene 提供的模糊查询基于编辑距离算法(Edit distance algorithm)。你可以在搜索词的尾部加上字符 ~ 来进行模糊查询。例如,查询语句 “think~” 返回所有包含和 think 类似的关键词的文档。清单 4 显示了如果利用 Lucene 的 API 进行模糊查询的代码。

    清单4:实现模糊查询

    //Test fuzzy search
                public void testFuzzySearch(String indexDirectory)throws Exception{
                Directory dir = FSDirectory.getDirectory(indexDirectory,false);
                IndexSearcher indexSearcher = new IndexSearcher(dir);
                String[] searchWords = {"text", "funny"};
                Query query;
                for(int i = 0; i < searchWords.length; i++){
                query = new FuzzyQuery(new Term("title",searchWords[i]));
                Hits results = indexSearcher.search(query);
                System.out.println(results.length() + "search results for query " + searchWords[i]);
                }
                }
                

    范围搜索(Range Search)

    范围搜索匹配某个域上的值在一定范围的文档。例如,查询 “age:[18 TO 35]” 返回所有 age 域上的值在 18 到 35 之间的文档。清单5显示了利用 Lucene 的 API 进行返回搜索的过程。

    清单5:测试范围搜索

    //Test range search
                public void testRangeSearch(String indexDirectory)throws Exception{
                Directory dir = FSDirectory.getDirectory(indexDirectory,false);
                IndexSearcher indexSearcher = new IndexSearcher(dir);
                Term begin = new Term("birthDay","20000101");
                Term end   = new Term("birthDay","20060606");
                Query query = new RangeQuery(begin,end,true);
                Hits results = indexSearcher.search(query);
                System.out.println(results.length() + "search results is returned");
                }
                

    在 Web 应用程序中集成 Lucene

    接下来我们开发一个 Web 应用程序利用 Lucene 来检索存放在文件服务器上的 HTML 文档。在开始之前,需要准备如下环境:

    1. Eclipse 集成开发环境
    2. Tomcat 5.0
    3. Lucene Library
    4. JDK 1.5

    这个例子使用 Eclipse 进行 Web 应用程序的开发,最终这个 Web 应用程序跑在 Tomcat 5.0 上面。在准备好开发所必需的环境之后,我们接下来进行 Web 应用程序的开发。

    1、创建一个动态 Web 项目

    1. 在 Eclipse 里面,选择 File > New > Project,然后再弹出的窗口中选择动态 Web 项目,如图二所示。

    图二:创建动态Web项目
    创建动态Web项目

    1. 在创建好动态 Web 项目之后,你会看到创建好的项目的结构,如图三所示,项目的名称为 sample.dw.paper.lucene。

    图三:动态 Web 项目的结构
    动态 Web 项目的结构

    2. 设计 Web 项目的架构

    在我们的设计中,把该系统分成如下四个子系统:

    1. 用户接口: 这个子系统提供用户界面使用户可以向 Web 应用程序服务器提交搜索请求,然后搜索结果通过用户接口来显示出来。我们用一个名为 search.jsp 的页面来实现该子系统。
    2. 请求管理器: 这个子系统管理从客户端发送过来的搜索请求并把搜索请求分发到搜索子系统中。最后搜索结果从搜索子系统返回并最终发送到用户接口子系统。我们使用一个 Servlet 来实现这个子系统。
    3. 搜索子系统: 这个子系统负责在索引文件上进行搜索并把搜索结构传递给请求管理器。我们使用 Lucene 提供的 API 来实现该子系统。
    4. 索引子系统: 这个子系统用来为 HTML 页面来创建索引。我们使用 Lucene 的 API 以及 Lucene 提供的一个 HTML 解析器来创建该子系统。

    图4 显示了我们设计的详细信息,我们将用户接口子系统放到 webContent 目录下面。你会看到一个名为 search.jsp 的页面在这个文件夹里面。请求管理子系统在包 sample.dw.paper.lucene.servlet 下面,类 SearchController 负责功能的实现。搜索子系统放在包 sample.dw.paper.lucene.search 当中,它包含了两个类,SearchManagerSearchResultBean,第一个类用来实现搜索功能,第二个类用来描述搜索结果的结构。索引子系统放在包 sample.dw.paper.lucene.index 当中。类 IndexManager 负责为 HTML 文件创建索引。该子系统利用包 sample.dw.paper.lucene.util 里面的类 HTMLDocParser 提供的方法 getTitlegetContent 来对 HTML 页面进行解析。

    图四:项目的架构设计
    项目的架构设计

    3. 子系统的实现

    在分析了系统的架构设计之后,我们接下来看系统实现的详细信息。

    1. 用户接口: 这个子系统有一个名为 search.jsp 的 JSP 文件来实现,这个 JSP 页面包含两个部分。第一部分提供了一个用户接口去向 Web 应用程序服务器提交搜索请求,如图5所示。注意到这里的搜索请求发送到了一个名为 SearchController 的 Servlet 上面。Servlet 的名字和具体实现的类的对应关系在 web.xml 里面指定。

    图5:向Web服务器提交搜索请求
    向Web服务器提交搜索请求

    这个JSP的第二部分负责显示搜索结果给用户,如图6所示:

    图6:显示搜索结果
    显示搜索结果

    1. 请求管理器: 一个名为 SearchController 的 servlet 用来实现该子系统。清单6给出了这个类的源代码。

    清单6:请求管理器的实现

    package sample.dw.paper.lucene.servlet;
                import java.io.IOException;
                import java.util.List;
                import javax.servlet.RequestDispatcher;
                import javax.servlet.ServletException;
                import javax.servlet.http.HttpServlet;
                import javax.servlet.http.HttpServletRequest;
                import javax.servlet.http.HttpServletResponse;
                import sample.dw.paper.lucene.search.SearchManager;
                /**
                * This servlet is used to deal with the search request
                * and return the search results to the client
                */
                public class SearchController extends HttpServlet{
                private static final long serialVersionUID = 1L;
                public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException{
                String searchWord = request.getParameter("searchWord");
                SearchManager searchManager = new SearchManager(searchWord);
                List searchResult = null;
                searchResult = searchManager.search();
                RequestDispatcher dispatcher = request.getRequestDispatcher("search.jsp");
                request.setAttribute("searchResult",searchResult);
                dispatcher.forward(request, response);
                }
                public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException{
                doPost(request, response);
                }
                }
                

    清单6中,doPost 方法从客户端获取搜索词并创建类 SearchManager 的一个实例,其中类 SearchManager 在搜索子系统中进行了定义。然后,SearchManager 的方法 search 会被调用。最后搜索结果被返回到客户端。

    1. 搜索子系统: 在这个子系统中,我们定义了两个类:SearchManagerSearchResultBean。第一个类用来实现搜索功能,第二个类是个JavaBean,用来描述搜索结果的结构。清单7给出了类 SearchManager 的源代码。

    清单7:搜索功能的实现

    package sample.dw.paper.lucene.search;
                import java.io.IOException;
                import java.util.ArrayList;
                import java.util.List;
                import org.apache.lucene.analysis.Analyzer;
                import org.apache.lucene.analysis.standard.StandardAnalyzer;
                import org.apache.lucene.queryParser.ParseException;
                import org.apache.lucene.queryParser.QueryParser;
                import org.apache.lucene.search.Hits;
                import org.apache.lucene.search.IndexSearcher;
                import org.apache.lucene.search.Query;
                import sample.dw.paper.lucene.index.IndexManager;
                /**
                * This class is used to search the
                * Lucene index and return search results
                */
                public class SearchManager {
                private String searchWord;
                private IndexManager indexManager;
                private Analyzer analyzer;
                public SearchManager(String searchWord){
                this.searchWord   =  searchWord;
                this.indexManager =  new IndexManager();
                this.analyzer     =  new StandardAnalyzer();
                }
                /**
                * do search
                */
                public List search(){
                List searchResult = new ArrayList();
                if(false == indexManager.ifIndexExist()){
                try {
                if(false == indexManager.createIndex()){
                return searchResult;
                }
                } catch (IOException e) {
                e.printStackTrace();
                return searchResult;
                }
                }
                IndexSearcher indexSearcher = null;
                try{
                indexSearcher = new IndexSearcher(indexManager.getIndexDir());
                }catch(IOException ioe){
                ioe.printStackTrace();
                }
                QueryParser queryParser = new QueryParser("content",analyzer);
                Query query = null;
                try {
                query = queryParser.parse(searchWord);
                } catch (ParseException e) {
                e.printStackTrace();
                }
                if(null != query >> null != indexSearcher){
                try {
                Hits hits = indexSearcher.search(query);
                for(int i = 0; i < hits.length(); i ++){
                SearchResultBean resultBean = new SearchResultBean();
                resultBean.setHtmlPath(hits.doc(i).get("path"));
                resultBean.setHtmlTitle(hits.doc(i).get("title"));
                searchResult.add(resultBean);
                }
                } catch (IOException e) {
                e.printStackTrace();
                }
                }
                return searchResult;
                }
                }
                

    清单7中,注意到在这个类里面有三个私有属性。第一个是 searchWord,代表了来自客户端的搜索词。第二个是 indexManager,代表了在索引子系统中定义的类 IndexManager 的一个实例。第三个是 analyzer,代表了用来解析搜索词的解析器。现在我们把注意力放在方法 search 上面。这个方法首先检查索引文件是否已经存在,如果已经存在,那么就在已经存在的索引上进行检索,如果不存在,那么首先调用类 IndexManager 提供的方法来创建索引,然后在新创建的索引上进行检索。搜索结果返回后,这个方法从搜索结果中提取出需要的属性并为每个搜索结果生成类 SearchResultBean 的一个实例。最后这些 SearchResultBean 的实例被放到一个列表里面并返回给请求管理器。

    在类 SearchResultBean 中,含有两个属性,分别是 htmlPathhtmlTitle,以及这个两个属性的 get 和 set 方法。这也意味着我们的搜索结果包含两个属性:htmlPathhtmlTitle,其中 htmlPath 代表了 HTML 文件的路径,htmlTitle 代表了 HTML 文件的标题。

    1. 索引子系统: 类 IndexManager 用来实现这个子系统。清单8 给出了这个类的源代码。

    清单8:索引子系统的实现

    package sample.dw.paper.lucene.index;
                import java.io.File;
                import java.io.IOException;
                import java.io.Reader;
                import org.apache.lucene.analysis.Analyzer;
                import org.apache.lucene.analysis.standard.StandardAnalyzer;
                import org.apache.lucene.document.Document;
                import org.apache.lucene.document.Field;
                import org.apache.lucene.index.IndexWriter;
                import org.apache.lucene.store.Directory;
                import org.apache.lucene.store.FSDirectory;
                import sample.dw.paper.lucene.util.HTMLDocParser;
                /**
                * This class is used to create an index for HTML files
                *
                */
                public class IndexManager {
                //the directory that stores HTML files
                private final String dataDir  = "c:\\dataDir";
                //the directory that is used to store a Lucene index
                private final String indexDir = "c:\\indexDir";
                /**
                * create index
                */
                public boolean createIndex() throws IOException{
                if(true == ifIndexExist()){
                return true;
                }
                File dir = new File(dataDir);
                if(!dir.exists()){
                return false;
                }
                File[] htmls = dir.listFiles();
                Directory fsDirectory = FSDirectory.getDirectory(indexDir, true);
                Analyzer  analyzer    = new StandardAnalyzer();
                IndexWriter indexWriter = new IndexWriter(fsDirectory, analyzer, true);
                for(int i = 0; i < htmls.length; i++){
                String htmlPath = htmls[i].getAbsolutePath();
                if(htmlPath.endsWith(".html") || htmlPath.endsWith(".htm")){
                addDocument(htmlPath, indexWriter);
                }
                }
                indexWriter.optimize();
                indexWriter.close();
                return true;
                }
                /**
                * Add one document to the Lucene index
                */
                public void addDocument(String htmlPath, IndexWriter indexWriter){
                HTMLDocParser htmlParser = new HTMLDocParser(htmlPath);
                String path    = htmlParser.getPath();
                String title   = htmlParser.getTitle();
                Reader content = htmlParser.getContent();
                Document document = new Document();
                document.add(new Field("path",path,Field.Store.YES,Field.Index.NO));
                document.add(new Field("title",title,Field.Store.YES,Field.Index.TOKENIZED));
                document.add(new Field("content",content));
                try {
                indexWriter.addDocument(document);
                } catch (IOException e) {
                e.printStackTrace();
                }
                }
                /**
                * judge if the index exists already
                */
                public boolean ifIndexExist(){
                File directory = new File(indexDir);
                if(0 < directory.listFiles().length){
                return true;
                }else{
                return false;
                }
                }
                public String getDataDir(){
                return this.dataDir;
                }
                public String getIndexDir(){
                return this.indexDir;
                }
                }
                

    这个类包含两个私有属性,分别是 dataDirindexDirdataDir 代表存放等待进行索引的 HTML 页面的路径,indexDir 代表了存放 Lucene 索引文件的路径。类 IndexManager 提供了三个方法,分别是 createIndex, addDocumentifIndexExist。如果索引不存在的话,你可以使用方法 createIndex 去创建一个新的索引,用方法 addDocument 去向一个索引上添加文档。在我们的场景中,一个文档就是一个 HTML 页面。方法 addDocument 会调用由类 HTMLDocParser 提供的方法对 HTML 文档进行解析。你可以使用最后一个方法 ifIndexExist 来判断 Lucene 的索引是否已经存在。

    现在我们来看一下放在包 sample.dw.paper.lucene.util 里面的类 HTMLDocParser。这个类用来从 HTML 文件中提取出文本信息。这个类包含三个方法,分别是 getContentgetTitlegetPath。第一个方法返回去除了 HTML 标记的文本内容,第二个方法返回 HTML 文件的标题,最后一个方法返回 HTML 文件的路径。清单9 给出了这个类的源代码。

    清单9:HTML 解析器

    package sample.dw.paper.lucene.util;
                import java.io.FileInputStream;
                import java.io.FileNotFoundException;
                import java.io.IOException;
                import java.io.InputStream;
                import java.io.InputStreamReader;
                import java.io.Reader;
                import java.io.UnsupportedEncodingException;
                import org.apache.lucene.demo.html.HTMLParser;
                public class HTMLDocParser {
                private String htmlPath;
                private HTMLParser htmlParser;
                public HTMLDocParser(String htmlPath){
                this.htmlPath = htmlPath;
                initHtmlParser();
                }
                private void initHtmlParser(){
                InputStream inputStream = null;
                try {
                inputStream = new FileInputStream(htmlPath);
                } catch (FileNotFoundException e) {
                e.printStackTrace();
                }
                if(null != inputStream){
                try {
                htmlParser = new HTMLParser(new InputStreamReader(inputStream, "utf-8"));
                } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                }
                }
                }
                public String getTitle(){
                if(null != htmlParser){
                try {
                return htmlParser.getTitle();
                } catch (IOException e) {
                e.printStackTrace();
                } catch (InterruptedException e) {
                e.printStackTrace();
                }
                }
                return "";
                }
                public Reader getContent(){
                if(null != htmlParser){
                try {
                return htmlParser.getReader();
                } catch (IOException e) {
                e.printStackTrace();
                }
                }
                return null;
                }
                public String getPath(){
                return this.htmlPath;
                }
                }
                

    5.在 Tomcat 5.0 上运行应用程序

    现在我们可以在 Tomcat 5.0 上运行开发好的应用程序。

    1. 右键单击 search.jsp,然后选择 Run as > Run on Server,如图7所示。

    图7:配置 Tomcat 5.0
    配置 Tomcat 5.0

    1. 在弹出的窗口中,选择 Tomcat v5.0 Server 作为目标 Web 应用程序服务器,然后点击 Next,如图8 所示:

    图8:选择 Tomcat 5.0
    选择 Tomcat 5.0

    1. 现在需要指定用来运行 Web 应用程序的 Apache Tomcat 5.0 以及 JRE 的路径。这里你所选择的 JRE 的版本必须和你用来编译 Java 文件的 JRE 的版本一致。配置好之后,点击 Finish。如 图9 所示。

    图9:完成Tomcat 5.0的配置
    完成Tomcat 5.0的配置

    1. 配置好之后,Tomcat 会自动运行,并且会对 search.jsp 进行编译并显示给用户。如 图10 所示。

    图10:用户界面
    用户界面

    1. 在输入框中输入关键词 “information” 然后单击 Search 按钮。然后这个页面上会显示出搜索结果来,如 图11 所示。

    图11:搜索结果
    搜索结果

    1. 单击搜索结果的第一个链接,页面上就会显示出所链接到的页面的内容。如 图12 所示.

    图12:详细信息
    详细信息

    现在我们已经成功的完成了示例项目的开发,并成功的用Lucene实现了搜索和索引功能。你可以下载这个项目的源代码(下载)。

    总结

    Lucene 提供了灵活的接口使我们更加方便的设计我们的 Web 搜索应用程序。如果你想在你的应用程序中加入搜索功能,那么 Lucene 是一个很好的选择。在设计你的下一个带有搜索功能的应用程序的时候可以考虑使用 Lucene 来提供搜索功能。

    是否非常羡慕别人的文章有机会冲上Digg头版,接着带来大量的访客呢?是否也希望自己的文章也能受到链接的宠爱,从而获得更高的PageRank呢?好,跟我来,让我帮你把文章接入到Social Software中。

    首先,你知道Social Software是什么吗?从广义上来说,就是能够让人们联系或聚集到一起的软件,老式的邮件列表或者新闻组也算是,然而通常我们用它指现代的blog和wiki平台。我们正在写的博客也算是Social Software,然而它还不够sociable,例如我们在博客园最多也就能把文章发布到首页,所以我们需要一个更加大范围的平台,例如Digg,而把你的网站接入到Digg仅需一个按钮——"Digg This!"按钮。

    Digg This?

    想象一下,你的访客来了,他觉得你这篇文章真是非常有价值,希望分享给其他人看,然后他就想点下那个写着"Digg This!"的按钮。慢着,按钮在哪?这位访客发现他当前用的这个浏览器没有这个按钮,你的文章页面上也没有这个按钮。要自己跑去Digg点击提交文章,然后把地址和标题复制粘贴过去,这多麻烦啊,所以他直接放弃了这个想法然后关掉了当前页。找不到"Digg This!"按钮的事情让访客感到沮丧(frustrated),他也就没心情看下去了,这样就跑掉了一个访客,多可惜啊。

    网站上的文章可以不好,但网站导航不能不好,让访客感到沮丧可是一大忌。什么是沮丧?我们来查一下frustrated,其意思就是你很想做一件事情然而总是有些障碍让你无法绕过去也无法达成目标。对于网站导航来说,让访客沮丧的情况通常发生在访客认定他要的信息或者功能就在你的网站上,然而通过链接导航或者搜索导航都无法到达目标。一个好的网站应该尽量提供用户所期望的信息和功能,并且提供清晰的导航,既然访客可能需要"Digg This!"按钮,我们就应该提供给他们。

    TechCrunch上个星期有条新闻标题为New York Times Surrenders To Social News,意思是“纽约时报向社会化新闻投降”,说的是New York Times网站上面部分版面的文章已经加入了共享功能,包括把文章共享到Digg、Facebook和Newsvine的链接。既然连New York Times这样的传统媒体巨头也加入了"Digg This!"按钮,我们又怎能不加?

    一个按钮就这么简单

    要加一个"Digg This!"按钮其实很容易,参考官方的帮助你就知道这仅需如下格式的一个链接:
    http://digg.com/submit?phase=2&url=&title=&bodytext=&topic=
    其中url参数用于传递地址,title参数用于传递标题,这两个最好就是文章原本的地址和标题。bodytext参数写你的推荐原因,topic参数表示推荐到的Digg栏目,这两个参数通常不在链接中提供而让读者到了Digg的提交页面后再填写。这四个参数都应该经过UrlEncode。

    如果使用Blogger之类的支持HTML模板的引擎,要放上一个这样的链接很简单,直接写一个链接然后加上模板所支持的参数替换就能把url和title两个参数搞定,可惜博客园的.Text引擎不支持HTML模板,也不支持参数替换。这样就沮丧啦?如果是的话,你的一群读者要跟着你一起沮丧了。我们还是一起努力把这个链接搞定吧,就一个链接而已,能够有多难?

    Do It Yourself

    你确认你是一个很有hack精神并且乐于DIY各种小玩意儿的人?那就跟我一起制作适用于博客园的"Digg This!"链接吧。如果你想先预览一下效果,可以看看本文正文下方的那个"Digg This!"链接。

    我这里为大家提供了一个用于自动添加"Digg This!"按钮的JavaScript,这段代码是我昨天花了两个小时调试出来的,能够在我当前使用的博客园模板上自动为文章页添加"Digg This!"链接。在博客园的设置中找到“页脚Html代码”这一栏,把代码贴进去就行了,别忘了要用<script type="text/javascript"></script>把代码包装起来。我不能确保这段代码一定能在你当前使用的模板上正常运行,所以这需要你有一定的JavaScript基础和DIY精神,根据你的实际情况作一些修改。

    在这里我可以解释一下这段代码的工作方式:通过对比首页的HTML和文章页的HTML,我发现了文章页的标题链接有一个特有的class="singleposttitle"属性,我就通过这个属性是否存在检测当前页面是首页还是文章页。代码执行时会循环迭代页面上所有的文章节点,如果它确认当前的是文章页,它就在第一个文章节点的尾部追加一个<div />上去,然后添加"Digg This!"等的链接。

    之所以要求你有一定的JavaScript基础和DIY精神,是因为我发现不同的模板输出的HTML结构差异非常大,你必须手动分析你当前所使用的模板输出的HTML,然后编写正确的JavaScript去识别页面性质并执行添加链接的任务。而且你对页面的添加功能需求细节可能和我不同,我的代码中有一句anchor.style.fontSize="14pt";是用于将当前文章标题链接放大的,可能你并不喜欢那么大的标题文字,也可能你希望在页面中别的地方添加"Digg This!"链接,这都依赖于你的JavaScript编写能力了。

    还有更有趣的吗?

    如果你不仅仅希望有"Digg This!"按钮,还希望有"Add to del.icio.us"、"Share on Facebook"、"Google Bookmark This"等按钮,你都可以自己加。分析第三方服务的调用方式不难,分析HTML结构特征也不难,你需要的就是一点点的DIY精神加上了解新鲜事物的激情,然后一切都会变得十分容易。

    前一段时间我在TechCrunch见到Snap的网页截图预览功能,然后就放到自己的博客中用,接着发觉越来越多博客园用户开始使用这东西,看来好东西真的是会一传十、十传百的。然而我们不能总是等看到别人有好东西了,自己再拿来用,我们需要有一点创新精神和动手能力,自己想一点新功能出来然后把它实现了。现在我为自己在博客园的文章添加了"Digg This!"按钮,或许迟点我会添加一些更有趣的功能上去,让它看上去更AJAX一些,这依赖于我何时能找到一台稳定服务的ASP.NET服务器。如果你对这方面的hacks(技巧)感兴趣并且希望长期关注的话,可以考虑订阅Cat in dotNET。如果你觉得这篇文章不错的话,你可以点击下面的"Add to del.icio.us"或"Google Bookmark This"进行收藏。

    我在科隆的阿姨和叔叔电话问我,如何让他们的新站获取google上的成功,我就将自己的一些秘诀告诉他们-如果你已经了解了SEO(搜索引擎优化),那么你可以不看;如果没有,有3个步骤会让你在搜索引擎获得好的排名。第一,创建好的内容。第二,让内容更方便阅读。第三,将你的内容告诉给别人。

    1. 创建好的内容
    你想获取好的Google排名,但你应该先问问自己:你的网站为什么排名靠前?面对激烈的网络竞争,那一个才是你想排名靠前的主要原因;你需要更多的来访 者,你想传播你个人认为非常重要的东西,你想要改变世界,或者你只是想卖点东西。你的网站为什么排名靠前?在Google和其他的搜索引擎眼中,你不过是 另一个站长—而搜索引擎将重要的部分提供给搜索者。所以最好坚信自己应该在Google排名靠前,而且是你自己在向搜索者提供着什么. 只要你做到这点你就可以执行第二步骤和第三步骤了,那么如何做呢:

    • 搜索引擎主要是去理解文本的.所以你要努力创建有趣的、有深度的文本形式的内容。使用Flash动作、视频或者一些图片不会起反作用,但也不会很好的帮助 搜索引擎。一个最好检测搜索引擎是否能够理解网页的方法就是看网页的源代码(右击选择“查看源文件”,其他浏览器可能不这么称呼)看看显示的文本情况。
    • 随后,可能会有大量的搜索查询会链接到你的网站。这时候一定要果断的放弃去优化每一个查询系列,最现实的策略是让你的网站拥有更多的相关内容。如果你的网站是和狗的用品相关的,那么你就写和狗、狗粮、狗的衣服款式、制作狗鞋的心得等内容;同时也可以放一到两篇的文艺复兴时期的养狗心得(尽量的多翻译出不同 的语言版本)。
    • 我们假设你真的出售狗的用品,而你的竞争对手与你一样,在那个网站也有很多相关内容。这时候你要的只有一件事,不仅仅创建相关内容,还要努力创建原创内 容;创建和狗名字相关的游戏;制作狗的增量图;提供给爱狗者有特色的壁纸下载。你应该明白—做一些别人没有想到的事情。有特色意味着网民有理由来你的 网站不是你竞争对手的网站。
    • 创建大量的原始内容是比较容易的工作。那么难的呢?哪就是建立在该领域的权威性,这没有什么捷径。当然,很可能你已经是专家了,这非常好,那么我建议你共享你的专家知识。人们会喜爱你的网站,是因为他们能够获得一些知识,很明显,这会改变他们的生活,虽然改变可能很少(可能是你带给他们的是做事情的有效方 式,或者你解决了他们某个问题)。相反,如果你不是既定领域的专家,我建议你创建一个博客,每天花费1个小时或再多点时间去研究相关的知识和新闻,边学边 写。大概半年以后,你应该会成为博客专题的专家。

    2. 让你的内容更方便阅读
    我们现在离开你关心的领域—狗的用品,或者其它你正在做的—转移到技巧方面来,以此让你的内容更方便阅读。和人类访问者希望看到的图片、文本内容 不同,搜索机器人看到的都是素材,比如HTML标签、网页标题、链接、粗体字标签等等。HTML的变更并不是很快,但你也许会做错事情的。所以你不要想着 亲自按照别人的想法去做,明了一个事物的双面性是必要的:

    • 每个页面只做一件理想的事.(这应该可以做好的).如果你有一个出售狗衣服的页面,那么这个页面应该有:包含了“狗衣服“这三个字的加粗部分;有狗衣服的 图片;你出售狗衣服的文章;该衣服属性的描述.另外,还要有一些网站相关内容的引导链接,例如狗所有用品的链接;描述你公司的链接(其中至少应该有公司的 地址、你的邮件等联系信息);一个返回主页的链接;等等。
    • 每个页面的标题应该是唯一的,并且确保它能够正确及令人信服的描述了该页的内容。如果你的页面是狗的蓝色服装,那么这个页面的标题就应该包含“狗的蓝色服 装“,比如说“来某某网站给狗买件蓝色的衣服“,而不应该是“某某网站“或者“某某网站销售猫、狗的流行款式的内外衣” 。
    • 谨慎使用带有类如h1和h2这样的粗体标签、p这样的段落、a这样的链接形式组成的 markup滚动,确保网站符合HTML4或者XHTML1的官方标准。有许多方式来显示网站的导航条,例如,使用动态的Flash、使用多层、使用下拉 框、或其它的JS脚本。这都可能犯错误,保持HTML的简单,只有在必要的时候才使用JS脚本,Frame框架,使用CSS来布置页面是最好的。
    • 确保每一个页面简单、稳定而且快速的读取。例如,不要使用不同的URL,不要创建重定向(redirects),不要使用过长且复杂的URL。简单而稳定 的网页即方便人们收藏或者引用,也会让搜索引擎,比如Google,做出可信任“Trust”的评价。如果你的网页是和狗的红色服装相关的,那么它的 URL就应该是“网站名/衣服/红色” 或者“网站名/产品/狗的红色服装.html”。

    想做好这些要点不是很容易而且有时会犯错,但你也不需要一次把所有事情都做好。理想的说,启用一个你或者你的网站开发者能够完全控制的CMS系统,并且制 作一个模版不失为一个好办法。但在启用前,最好做一定的测试,比如增加新的产品或者文章、调整所有的粗体字的大小、或者替换所有页面的底部,如果你为此而 头痛,那么就证明系统选错了。

    3. 将你的内容告诉别人
    在以前,人们为了优化他们的网页,总是加入很多很多关键词。但今天的搜索引擎—对于搜索者来说非常幸运—已经不是那么好骗的了。Google和其 它的搜索引擎除了关注你的网站,会更关注其它的网站来判断你的网站的可信度…尤其是检查链接到你网站的站点。因此你的网站的可信度越高,那么在查询的 时候它的排名就越高。那么如何获得其它网站的链接呢?你已经建立了优秀的内容(第一步骤),你也让你的网站可读性更强而且容易去链接(第二步骤),那么剩 下的工作就是让别人以链接你为荣,不是出于怜悯、也不是因为你保证交换链接、更不是因为你买下这些链接,只是因为你的内容能够满足自己的访问者。

    • 线下活动:去会议中心、交易会,任何人们可能产生兴趣并希望了解网站的地方。
    • 线上活动:制作网站内容的邮寄列表、参与新闻讨论组和相关的论坛,寻找相关的分类目录(比如Dmoz)并且提交自己的站点,和相关的管理员或者博客联系。 但是千万不要过渡吹嘘,这种自私自利会被认做是作弊“spam”。相反,你应该花点时间去帮助别人,例如,你可以给另一个网站写篇文章。千万不要象扔垃圾 似的到处乱贴你网站的URL,那样你一定会被人家当垃圾清理出去的。
    • 其它活动:电视台是不是正在讨论网站呢?好的,写封信给他们,介绍下自己的网站,这没什么坏处的。努力的去挖掘对网站感兴趣的人,比如报纸、电台、杂志或 者其它方式。也有一些其它的方式能够有所帮助,比如做点滑稽的事(在背上涂好网站的名字然后去足球场上裸奔)或者在值得签名的地方签名(写本书、拍照,无 论是否裸照)。对于狗的用品这个标题来说,你甚至可以训练你的狗,让它喊你的网站名,然后把整段视频上传到Youtube上,估计你肯定是头条。
    • 如果你有多余的钱,那么不妨在搜索引擎上做广告,例如Google AdWords。但这并不等同于真正的排名结果,只是在网站的幼年期帮助你的网站更突出一些。

    另外,还要做什么?

    假如你完成了上面的工作—创建好的内容、让内容更方便阅读、将内容告诉给别人—那么你可以歇息一会了,然后回来继续完善你的网站。在起初的一两个 月不需要担心Google的收录,事实上,根本别在意Google的收录。在起步阶段也许你的网站根本不会出现在搜索引擎中,也许会一直都不出现,而你的 竞争对手却排名很好…别忘记这需要时间。(不要忘记自问下想排名更好的原因,除此之外还应该做什么—如果你的竞争对手是一家大型的、广为人知的、 运转良好的而且是可信任的网站,那么它的排名理应在你之前)。

    在几个月后,该干什么呢?就是去监督和审查你的网站,去发现人们发现你网站的各种关键词。不妨使用网站统计程序,比如 Google Analytics来协助你完成这个工作。这些统计程序需要你在网站中插入跟踪的代码,数天后,你就可以发现你哪一个页面最优秀,哪一个页面受到了搜索者的青睐。然后在后面的工作中根据这些数据进行调整。

    例如,当你发现非常青睐“制作狗鞋的心得“的页面,那么你就可以多创作这样的内容,并且在“制作狗鞋的心得“的页面加入一个看得见的链接,如“买狗鞋“。

    另一个例子,当你发现总有一群人每天通过搜索“狗的蓝色衣服”进入你的网站,而他们离开的页面是出售“狗的蓝色衣服”和“狗的红色衣服”的页面—换句 话说,也就是你的数据流程让人产生了“误解”。但是呢,你提供的“狗的蓝色衣服”的页面一定存在的网站其他的地方,当你理解了这种情况,你就应该在人们进 入网站的地方加上一个链接,指向销售“狗的蓝色衣服”和“狗的红色衣服”的页面。

    当然,一旦这些搜索引擎优化的基本工作完成后,你可以搜索更多的优化知识、阅读更多的搜索博客、或者雇用一个搜索引擎优化的顾问。然而,一定要当心,你阅 读的一些高级的秘籍可能会让你后院起火、或者你雇错了搜索引擎优化的顾问。下面是一个快速审查“优化“的列表,你应该尽力避免:

    • 不要在不相关的地方堆积大量关键词
    • 不要牺牲来访者而专优化搜索引擎;如果有人建议你增加几个域名来协助排名,而你认为过多的域名会误导你的顾客,那就别加。
    • 不要相信那些保证”快速排名第一”,”保证进入前10″之类的鬼话
    • 不要链接那些许诺反链回来的网站
    • 不要链接那些许诺付费的网站,除非你清楚知道你正在干什么(例如,你了解”坏邻居”,”nofollow的属性”,”PageRank”,”JS广告和文本链接的区别”,”遭遇Google惩罚意味着什么?”)
    • 不要创建多份内容一样的页面
    • 不要让别人在你的网站留下垃圾的URL;如果你有一个论坛,坚持与Spam作战
    • 不要在别人的网站乱丢你的URL
    • 不要使用廉价的服务器,他们大多不能为你统计流量、建立相应的管理工具—如果你想拥有好的网站和服务器,你应该付钱
    • 不要过度担心页面的Meta标签、Meta关键词或者其他。你的时间应该用在创建好的内容上
    • 不要使用那些自动提交到分类目录、搜索引擎等等的工具
    • 不要向搜索引擎和用户提交不同的内容;尤其是隐藏文本
    • 不要过度优化,如果搜索引擎认定网站过度优化,那么他们肯定会惩罚你的
    • 简单的说,不要自以为自己比搜索引擎更聪明(除非你决定花费毕生精力与之奋斗);搜索引擎已经有偿的奖赏那些举报作弊的站长,从长远的眼光来看,作弊成功的机会很小

     

    我在科隆的阿姨和叔叔电话问我,如何让他们的新站获取google上的成功,我就将自己的一些秘诀告诉他们-如果你已经了解了SEO(搜索引擎优化),那么你可以不看;如果没有,有3个步骤会让你在搜索引擎获得好的排名。第一,创建好的内容。第二,让内容更方便阅读。第三,将你的内容告诉给别人。

    1. 创建好的内容
    你想获取好的Google排名,但你应该先问问自己:你的网站为什么排名靠前?面对激烈的网络竞争,那一个才是你想排名靠前的主要原因;你需要更多的来访 者,你想传播你个人认为非常重要的东西,你想要改变世界,或者你只是想卖点东西。你的网站为什么排名靠前?在Google和其他的搜索引擎眼中,你不过是 另一个站长—而搜索引擎将重要的部分提供给搜索者。所以最好坚信自己应该在Google排名靠前,而且是你自己在向搜索者提供着什么. 只要你做到这点你就可以执行第二步骤和第三步骤了,那么如何做呢:

    • 搜索引擎主要是去理解文本的.所以你要努力创建有趣的、有深度的文本形式的内容。使用Flash动作、视频或者一些图片不会起反作用,但也不会很好的帮助 搜索引擎。一个最好检测搜索引擎是否能够理解网页的方法就是看网页的源代码(右击选择“查看源文件”,其他浏览器可能不这么称呼)看看显示的文本情况。
    • 随后,可能会有大量的搜索查询会链接到你的网站。这时候一定要果断的放弃去优化每一个查询系列,最现实的策略是让你的网站拥有更多的相关内容。如果你的网站是和狗的用品相关的,那么你就写和狗、狗粮、狗的衣服款式、制作狗鞋的心得等内容;同时也可以放一到两篇的文艺复兴时期的养狗心得(尽量的多翻译出不同 的语言版本)。
    • 我们假设你真的出售狗的用品,而你的竞争对手与你一样,在那个网站也有很多相关内容。这时候你要的只有一件事,不仅仅创建相关内容,还要努力创建原创内 容;创建和狗名字相关的游戏;制作狗的增量图;提供给爱狗者有特色的壁纸下载。你应该明白—做一些别人没有想到的事情。有特色意味着网民有理由来你的 网站不是你竞争对手的网站。
    • 创建大量的原始内容是比较容易的工作。那么难的呢?哪就是建立在该领域的权威性,这没有什么捷径。当然,很可能你已经是专家了,这非常好,那么我建议你共享你的专家知识。人们会喜爱你的网站,是因为他们能够获得一些知识,很明显,这会改变他们的生活,虽然改变可能很少(可能是你带给他们的是做事情的有效方 式,或者你解决了他们某个问题)。相反,如果你不是既定领域的专家,我建议你创建一个博客,每天花费1个小时或再多点时间去研究相关的知识和新闻,边学边 写。大概半年以后,你应该会成为博客专题的专家。

    2. 让你的内容更方便阅读
    我们现在离开你关心的领域—狗的用品,或者其它你正在做的—转移到技巧方面来,以此让你的内容更方便阅读。和人类访问者希望看到的图片、文本内容 不同,搜索机器人看到的都是素材,比如HTML标签、网页标题、链接、粗体字标签等等。HTML的变更并不是很快,但你也许会做错事情的。所以你不要想着 亲自按照别人的想法去做,明了一个事物的双面性是必要的:

    • 每个页面只做一件理想的事.(这应该可以做好的).如果你有一个出售狗衣服的页面,那么这个页面应该有:包含了“狗衣服“这三个字的加粗部分;有狗衣服的 图片;你出售狗衣服的文章;该衣服属性的描述.另外,还要有一些网站相关内容的引导链接,例如狗所有用品的链接;描述你公司的链接(其中至少应该有公司的 地址、你的邮件等联系信息);一个返回主页的链接;等等。
    • 每个页面的标题应该是唯一的,并且确保它能够正确及令人信服的描述了该页的内容。如果你的页面是狗的蓝色服装,那么这个页面的标题就应该包含“狗的蓝色服 装“,比如说“来某某网站给狗买件蓝色的衣服“,而不应该是“某某网站“或者“某某网站销售猫、狗的流行款式的内外衣” 。
    • 谨慎使用带有类如h1和h2这样的粗体标签、p这样的段落、a这样的链接形式组成的 markup滚动,确保网站符合HTML4或者XHTML1的官方标准。有许多方式来显示网站的导航条,例如,使用动态的Flash、使用多层、使用下拉 框、或其它的JS脚本。这都可能犯错误,保持HTML的简单,只有在必要的时候才使用JS脚本,Frame框架,使用CSS来布置页面是最好的。
    • 确保每一个页面简单、稳定而且快速的读取。例如,不要使用不同的URL,不要创建重定向(redirects),不要使用过长且复杂的URL。简单而稳定 的网页即方便人们收藏或者引用,也会让搜索引擎,比如Google,做出可信任“Trust”的评价。如果你的网页是和狗的红色服装相关的,那么它的 URL就应该是“网站名/衣服/红色” 或者“网站名/产品/狗的红色服装.html”。

    想做好这些要点不是很容易而且有时会犯错,但你也不需要一次把所有事情都做好。理想的说,启用一个你或者你的网站开发者能够完全控制的CMS系统,并且制 作一个模版不失为一个好办法。但在启用前,最好做一定的测试,比如增加新的产品或者文章、调整所有的粗体字的大小、或者替换所有页面的底部,如果你为此而 头痛,那么就证明系统选错了。

    3. 将你的内容告诉别人
    在以前,人们为了优化他们的网页,总是加入很多很多关键词。但今天的搜索引擎—对于搜索者来说非常幸运—已经不是那么好骗的了。Google和其 它的搜索引擎除了关注你的网站,会更关注其它的网站来判断你的网站的可信度…尤其是检查链接到你网站的站点。因此你的网站的可信度越高,那么在查询的 时候它的排名就越高。那么如何获得其它网站的链接呢?你已经建立了优秀的内容(第一步骤),你也让你的网站可读性更强而且容易去链接(第二步骤),那么剩 下的工作就是让别人以链接你为荣,不是出于怜悯、也不是因为你保证交换链接、更不是因为你买下这些链接,只是因为你的内容能够满足自己的访问者。

    • 线下活动:去会议中心、交易会,任何人们可能产生兴趣并希望了解网站的地方。
    • 线上活动:制作网站内容的邮寄列表、参与新闻讨论组和相关的论坛,寻找相关的分类目录(比如Dmoz)并且提交自己的站点,和相关的管理员或者博客联系。 但是千万不要过渡吹嘘,这种自私自利会被认做是作弊“spam”。相反,你应该花点时间去帮助别人,例如,你可以给另一个网站写篇文章。千万不要象扔垃圾 似的到处乱贴你网站的URL,那样你一定会被人家当垃圾清理出去的。
    • 其它活动:电视台是不是正在讨论网站呢?好的,写封信给他们,介绍下自己的网站,这没什么坏处的。努力的去挖掘对网站感兴趣的人,比如报纸、电台、杂志或 者其它方式。也有一些其它的方式能够有所帮助,比如做点滑稽的事(在背上涂好网站的名字然后去足球场上裸奔)或者在值得签名的地方签名(写本书、拍照,无 论是否裸照)。对于狗的用品这个标题来说,你甚至可以训练你的狗,让它喊你的网站名,然后把整段视频上传到Youtube上,估计你肯定是头条。
    • 如果你有多余的钱,那么不妨在搜索引擎上做广告,例如Google AdWords。但这并不等同于真正的排名结果,只是在网站的幼年期帮助你的网站更突出一些。

    另外,还要做什么?

    假如你完成了上面的工作—创建好的内容、让内容更方便阅读、将内容告诉给别人—那么你可以歇息一会了,然后回来继续完善你的网站。在起初的一两个 月不需要担心Google的收录,事实上,根本别在意Google的收录。在起步阶段也许你的网站根本不会出现在搜索引擎中,也许会一直都不出现,而你的 竞争对手却排名很好…别忘记这需要时间。(不要忘记自问下想排名更好的原因,除此之外还应该做什么—如果你的竞争对手是一家大型的、广为人知的、 运转良好的而且是可信任的网站,那么它的排名理应在你之前)。

    在几个月后,该干什么呢?就是去监督和审查你的网站,去发现人们发现你网站的各种关键词。不妨使用网站统计程序,比如 Google Analytics来协助你完成这个工作。这些统计程序需要你在网站中插入跟踪的代码,数天后,你就可以发现你哪一个页面最优秀,哪一个页面受到了搜索者的青睐。然后在后面的工作中根据这些数据进行调整。

    例如,当你发现非常青睐“制作狗鞋的心得“的页面,那么你就可以多创作这样的内容,并且在“制作狗鞋的心得“的页面加入一个看得见的链接,如“买狗鞋“。

    另一个例子,当你发现总有一群人每天通过搜索“狗的蓝色衣服”进入你的网站,而他们离开的页面是出售“狗的蓝色衣服”和“狗的红色衣服”的页面—换句 话说,也就是你的数据流程让人产生了“误解”。但是呢,你提供的“狗的蓝色衣服”的页面一定存在的网站其他的地方,当你理解了这种情况,你就应该在人们进 入网站的地方加上一个链接,指向销售“狗的蓝色衣服”和“狗的红色衣服”的页面。

    当然,一旦这些搜索引擎优化的基本工作完成后,你可以搜索更多的优化知识、阅读更多的搜索博客、或者雇用一个搜索引擎优化的顾问。然而,一定要当心,你阅 读的一些高级的秘籍可能会让你后院起火、或者你雇错了搜索引擎优化的顾问。下面是一个快速审查“优化“的列表,你应该尽力避免:

    • 不要在不相关的地方堆积大量关键词
    • 不要牺牲来访者而专优化搜索引擎;如果有人建议你增加几个域名来协助排名,而你认为过多的域名会误导你的顾客,那就别加。
    • 不要相信那些保证”快速排名第一”,”保证进入前10″之类的鬼话
    • 不要链接那些许诺反链回来的网站
    • 不要链接那些许诺付费的网站,除非你清楚知道你正在干什么(例如,你了解”坏邻居”,”nofollow的属性”,”PageRank”,”JS广告和文本链接的区别”,”遭遇Google惩罚意味着什么?”)
    • 不要创建多份内容一样的页面
    • 不要让别人在你的网站留下垃圾的URL;如果你有一个论坛,坚持与Spam作战
    • 不要在别人的网站乱丢你的URL
    • 不要使用廉价的服务器,他们大多不能为你统计流量、建立相应的管理工具—如果你想拥有好的网站和服务器,你应该付钱
    • 不要过度担心页面的Meta标签、Meta关键词或者其他。你的时间应该用在创建好的内容上
    • 不要使用那些自动提交到分类目录、搜索引擎等等的工具
    • 不要向搜索引擎和用户提交不同的内容;尤其是隐藏文本
    • 不要过度优化,如果搜索引擎认定网站过度优化,那么他们肯定会惩罚你的
    • 简单的说,不要自以为自己比搜索引擎更聪明(除非你决定花费毕生精力与之奋斗);搜索引擎已经有偿的奖赏那些举报作弊的站长,从长远的眼光来看,作弊成功的机会很小

    不管怎么说,祝好运。

    作为www.GouNet.cn( 购网之家!)的站长,对每个在本网站发布的个人网站进行流量评估是我的常规工作。以下总结自己的几点体会,供买站者或广告主参考,不对之处,请予指正。

    一.观察Alexa

    Alexa虽然不准,但还是能够看出很多东西。因此我在看了网站内容、大致感兴趣之后,首先看Alexa统计。
       第一步.流量估算
           通常先使用http://alexa.chinaz.com/估算流量,http://alexa.chinaz.com/有时运行不正常,就改用http://www.alexa.com/,通过百万人数到达率(Reach per million users)估算流量。
    在无作弊情况下,Alexa数据体现的流量信息与网站的访问人群特征密切相关。例如站长类网站,因为访客带Alexa工具条的多,百万人数到达率比普通站要高5-10倍。比较可靠的方法是用已知的同类的无作弊网站来对比。例如,对于QQ类站,Reach per million users等于35时我估算为1万ip,而对于站长类站,Reach per million users要达到160才估算为1万ip。
    Alexa数据可以快速过滤低流量站,也可以对某站的“假设流量”有个大致印象。
       第二步.PV检查
    每个访客的平均PV,可以用来检测作弊和弹窗流量。平均PV的参考值,不同类型的站点有很大不同,例如网址站PV很低,小说站PV很高。如果PV比同类网站的正常值低很多,多半有刷流量的作弊行为,或者外部有弹窗流量注入,外部弹窗流量注入并非网站真实能力,因此一钱不值。
         对于平均PV严重低于参考值的站,就放弃参考其alexa数据,只看第三方统计。
       第三步.曲线检查
        曲线检查用http://www.alexa.com/来做比较好,比chinaz看得更清晰。
        首先是看流量持续的时间。如果是刚做不久的新站串得飞快,或者老站流量突然串升,就很值得留神,从坏处想可能做了广告或者刷了排名,从好处想有关键词爬到百度的前面,但因为时间太短,还不稳定。通过第三方统计可以验证。
    其次是看曲线形状。曾经看过一个流量很高的新站,alexa曲线突然急升,然后就保持平稳,一看就知道是刷出来的。如果做广告或靠口碑,流量曲线不会是这样。这样的站跑得太急,以后想纠正也不容易,永远被人质疑。

    二.观察第三方统计  
    51.lacnzz51yes等的免费统计,虽然离对网站经营有指导价值的统计系统还差得很远,但对于估计流量概况而言提示的信息还是足的。
    不让看第三方统计的,或者虽然让看独立查看帐号、但开放的信息少、没有来路细节的,多有隐情,不需要再谈。
       第一步.看访问来源
    如果有高比例的“自行输入网址”,则先去检查“回访比例”,如果1次以上老客户回访比例很低,而“自行输入网址”很高,则多半有外部弹窗、夹页或刷流量作弊。另外还可以参考PV,或者看当前在线的人是从哪来停留在哪里。
    如果有高比例流量来自论坛联盟、网摘等,应注意到这可能需要站长一次次努力去创造,未必是恒定流量。通常要先判断这是web2.0性质的访客所为还是站长的主观努力,如果是后者就不考虑这部分流量。
    如果有高比例流量来自某个或某几个站,要看这网站是什么性质,是否可以长期稳定支持。
        第二步.看搜索主题词分布
    如果高比例流量来自百度,则看关键词分布。
    如果关键词很分散,要看是否跟网站主题匹配,越匹配越有价值。我见过一个流量上升很快的站,流量多从百度来,关键词却是与主题关系不大的各种各样的火车时刻表,这样的流量品质就低,无论买站还是做广告价值都不大。
    如果主题词很集中,要看时间分布,如果在百度前排的时间太短,那么就不太稳定。百度反seo做得好,seo越成功越危险,最理想的是长期排在前面,seo又不过分,甚至故意不seo、取消seo。百度稳定性对于广告影响不大,对于买站则要特别留神。
       第三步.看被访页
        被访页能够体现访客的行为,对于发现通过外部输血或作弊带来的流量有一定参考作用。具体情况具体分析,这里不多谈。

    三.观察论坛      
    论坛、贴吧、爱问等,是观察网站人气的重要窗口。如果你要卖一个站或者寻求广告主,没好的人气就别放论坛,不然容易有副作用。
    观察论坛时,不用理会其显示有多少贴子、多少会员、多少在线人数,看最近2天的发贴和跟贴情况即可。如果每个版发贴的人很集中,总是那么几个人,基本可以断定这个论坛没有形成人气,其母站的流量可以想见。

    四.观察广告收入      
    可以先看站长挂了哪些广告,再问他广告收入情况,必要时请他截取图象验证。通常能发现一些上述方法看不到的问题。
    最大的问题是流量品质低,站长用各种方法搜刮来的流量,有数量没质量,无论是在上面做广告,还是用来买来喂青蛙(参见《你的网站值多少钱》),流量转化率都很低。总体来说,随着采集的盛行、网摘和论坛联盟的流行、引擎依赖性对做站思想的影响、联盟广告对流量的催生…,现在流量如打了生长素的猪,味道已经大不如前了。当判断一个网站的购买价值或广告价值时,不但要看流量,还要考虑他流量的品质,准确地说是考虑流量性质跟你的目的的接近度。
    按照相同逻辑,站长如同养猪喂鸡的农民,是时候考虑养些瘦肉型猪、跑地鸡了,也许赚更多。

    早期搜寻引擎

    搜寻引擎发韧于90年代中期。此时第一代搜寻引擎开始对因特网分门别类。一开始,所有网站员得做的事只有提交所属网站到各家搜寻引擎。这些引擎跑一些蜘蛛机器人(spider)──根据超链结爬过网站的程序──并且储存所找到的资料。搜寻引擎根据标题后处理这些资讯,并且根据这些分析结果提供服务。随著在线文件数目日积月累,越来越多网站员意识到基本搜寻 (organic search, 亦翻为随机搜寻 *注一) 条目的重要性,所以较普及的搜寻引擎公司开始整理他们的列表,以显示根据最洽当适合的网页为优先。搜寻引擎与网站员的战争就此开始,并延续至今。

    一开始搜寻引擎是被网站员本身牵著走的。早期版本的搜寻算法有赖于网站员提供资讯,如属哪类、关键字的汇签(meta tag)。当某些网站员开始滥用汇签,造成该网页排名与连结无关时,搜寻引擎开始舍弃汇签并发展更复杂的排名算法。由于数繁不及备载,仅列举数个分析目标如下:

    • 在标题签里的文字,如 <h1>引擎</h1>
    • 域名,如 wikipedia.org
    • 统一资源定位符下属的目录与档名,如 http://zh.wikipedia.org/wiki/Seo
    • 关键字密度
    • 关键字接近度,如 ‘软盘、软碟’ ‘硬盘、硬盘’
    • 图形连结的 Alt 属性,如 <img src="…/powerby_mediawiki_88×31.png" alt="MediaWiki" />

    由于这些都还在网站员的眼皮下打滚,搜寻引擎陆陆续续碰到诸多滥用与操纵排名等难题。为了要提供较好的结果给使用者,搜寻引擎必须调适到让他们的搜索结果表现出最适当的结果,而非某些不道德的网络员产生的、只有塞满关键字的无用网页。由此,新种类的搜寻引擎应运而生。

    注一:随机搜寻实在是误翻,与原始所指相差太远。

    [编辑] 基本搜寻引擎

    Google 由两名在斯坦福大学的博士生佩吉 (Larry Page) 和布林 (Sergey Brin) 开始。他们带来了一个给网页评估的新概念。这个概念, 称网页级别 (PageRank), 是从Google 算法[1]重要的开端 。网页级别十分倚赖导入链结 (incoming link) ,并利用这种每个导入某网页的链结相当于给该网页价值投一票的理论建立起逻辑系统。越多导入链结意味著该网页越有“价值”。而每个导入链结本身价值直接根据该链结从何而来的网页级别,以及相反的该页导出链结 (outgoing link) 。

    在网页级别帮助下,Google 在服务相关的结果上证明它相当优异。Google 成为了最普遍和最成功的搜索引擎。由于网页级别度量了站点外因子, Google 感到它会比页内因子难以动手脚。

    然而道高一呎魔高一丈。网站员们已经开发出对付Inktomi 搜索引擎的链结操作工具和计划。这些方法证明对Google 算法一样管用。许多站集中于交换、买卖大量链接。随著‘网站员寻求获取链结只单单要影响Google送更多流量给该站,而不管是否对站点访客有用否’这种行为增加,网页级别对原始算法的信赖度渐渐被破坏了。

    此时,是Google 和其它查寻引擎对广大范围的站外因子仔细检视的时候。开发更加聪明的算法有其他原因。因特网已经膨胀到拥有非技术的广大族群。他们经常无法使用先进的提问技术来取的资讯;而且他们得面对比起发展早期更庞大资料、更复杂的索引。搜寻引擎必须开发具备预测性、语义性语言性启发性算法。

    目前,网页级别的缩小版仍然被显示在Google 工具条上,不过网页级别只不过是Google 考虑在网页分级时超过100 个因素里中的一个。

    今天,大多数搜寻引擎对它们的如何评等的算法保持秘密。搜索引擎也许使用上百因素在排列目录;每个因素本身和因素所占比重可能不断的在改变。

    大部分当代搜寻引擎优化的思路──哪些有效、哪些没效──这些很大部分在于观察与根据内线消息来的猜测。某些优化员得执行控制下的实验以取得不同优化方法的结果。

    尽管如此,以下是搜寻引擎发展它们算法时的一些考虑,另,这份Google 专利清单[2]也许读者可猜出他们会走哪条路线:

    • 站点的年龄
    • 自该网域注册后过多久
    • 内容的年龄
    • 新内容增加的规律性
    • 链接的年龄和连接站点的名誉
    • 一般站内因素
    • 负面站内因素 (例如,太多关键字汇标(meta tag),很显然被优化过,会对站点造成伤害)
    • 内容的独特性
    • 使用于内容的相关术语 (搜寻引擎关联到的术语的方式视同如何关联到网页的主要内容)
    • Google网页级别 (只被使用在Google 的算法)
    • 外在链接、外部链接的链结文字、在那些和在站点/网页包含的那些链接
    • 引证和研究来源(表明内容是研究性质)
    • 在搜索引擎数据库里列举的词根与其相关的术语(如 finance/financing)
    • 导入的逆向链结,以及该链结的文字
    • 一些导入链结的负面计分 (或许那些来自低价值页、被交换的逆向链结等)
    • 逆向链结取得速率:太多太快意味著“不自然”的链结购买活动
    • 围绕在导出链结、导入的逆向链结周围的文字。例如一个链结如果被 "Sponsored Links" (赞助商连结) 包围,该链结可能会被忽略。
    • 用 "rel=nofollow" 建议搜寻引擎忽略该链接
    • 在站点该文件的结构深度
    • 从其他资源收集的网格表,譬如监视当搜寻结果导引用户到某页后,用户有多频繁按浏览器的返回钮
    • 从来源像:Google AdWords/AdsenseGoogle 工具条等程序收集的网格表
    • 从第三方资料分享协议收集的网格资料 (譬如监测站点流量的统计程序提供商)
    • 删除导入链结的速率
    • 使用子网域、在子网使用关键字和内容质量等等,以及从这些活动来的负面计分
    • 和主文件语意上的连结
    • 文件增加或改动的速率
    • 主机服务商 IP 和该 IP 旗下其它站点的数量/质量
    • 其他链结站会员 (link farm / link affiliation) 与被链结的站 (他们分享IP吗? 有一个共同的邮递地址在"联络我们 (Contact Us)" 页吗?)
    • 技术细节像利用301重定向被移除的网页、对不存在网页显示404服务器标头而非200服务器标头、适当的利用 robots.txt
    • 主机服务商正常运行时间
    • 是否站点对不同类的用户显示不同的内容 (掩饰 (cloaking))
    • 未及时矫正、无效的导出的链结
    • 不安全或非法内容
    • HTML代码品质,以及错误出现数
    • 由搜寻引擎自他们搜寻结果观察到的实际点击通过率评等
    • 由最常存取搜寻结果的人手动评等

    [编辑] 搜寻引擎优化和搜寻引擎之间关系

    在第一代搜寻引擎发表后,搜寻引擎操作员变得对搜寻引擎优化社区感兴趣。在一些早期搜寻引擎, 譬如INFOSEEK, 要得到第一名不过是把顶尖的网页代码抓下、放在您的网站、并提交个URL让搜寻引擎立即索引并排名该页这么简单。

    由于搜寻本身的高价值和标定性,搜寻引擎和搜寻引擎优化员间自始便存在对抗的关系。最近一次于2005 年召开的AirWeb年会,旨在谈论缩小这种敌对关系差距,和如何最小化某些太过于侵略性优化造成的损坏效果。

    某些更具侵略性的优化员产生自动化的站点,或者使用某些最终会让该网域被搜寻引擎扫地出门的技术。而大多数优化公司则销售长期、低风险的策略服务,而且大部分使用高风险战略的优化公司,则在他们旗下的会员点使用、产生商业线索、或者纯内容站点,而非让它们客户站亲身涉险。

    这里提供一个使用侵略性优化技术的优化公司让他们客户被取缔的案例。华尔街时报描述了某个使用高风险技术和涉嫌没有透露客户得承担那些风险的公司[3]。Wired报告了该公司起诉某部落格,因为提及该公司被取缔[4]。Google 的克特斯 (Matt Cutts) 稍后确认Google 确实取缔了Traffic Power以其他们的客户群[5]

    某些搜寻引擎对搜寻引擎优化产业提供了援助,而且是常常是优化会议和研讨会的赞助商和来访贵宾。实际上,自从付费收录 (paid inclusion) 降临,一些搜寻引擎现在在网站优化社区的健康有了既得利益。所有主要搜寻引擎都提供资讯/指南以协助站点优化: GoogleYahoo, 和MSN 。Google提供了Sitemaps 程序帮助网站员学习如果Google 有任何问题检索他们的网站时该如何做,并且提供Google流量与您网站关系间无价的丰富资料。雅虎的SiteExplorer,旨在提供一个免费方式递交您的URL, 该方法能让你决定打算让雅虎索引多少页、索引多深。雅虎的 Ambassador Program 与Google的 Google Advertising Professionals 提供专家级的认证。

    [编辑] 搜寻引擎入手

    新站点不需要"提交"到搜寻引擎才能登记上市。一个来自于以建立好的、其他站点的简单链结就会让搜寻引擎拜访新站点,并且开始‘爬’过该站内容。它可能得花几天甚或几周从这样一个已建立站点取得连结,并使所有主要搜索引擎开始拜访并索引新站点。

    一旦搜索引擎发现了新站点,它一般将拜访和开始索引该站,直到所有标准的 <a href> 超连结被链结的页索引到为止。只能透过Flash或JavaScript才能拜访的链结可能不会被蜘蛛机器人找到。

    当搜索引擎的蜘蛛机器人爬过一个站点时会取决相当数量的不同因子,并且该站的许多页可能不会被索引到除非它们网页级别、连结、或流量增加到一个程度。从站点的根目录到该页的距离,以及其它比重考量,也许也是决定是否该页得到检索的因素。Cho et al.(Cho et al. 1998) [6] 描述了哪些页会被拜访、哪些会收入搜寻引擎索引的决定标准。

    网站员可透过在网域根目录里标准robots.txt档案指示蜘蛛机器人不索引某些文件或目录。标准的实现要求是搜索引擎在拜访这个网域时参考这个文件,虽然搜索引擎的蜘蛛机器人当它拜访某站点网页时将保留这个文件的快取拷贝,并且更新速度没像网站员那么快。网站发展人员可能使用这个特性防止某些页,譬如购物车或其它动态、特定使用者的内容出现在搜索引擎结果中,并且防止机器人进入死循环和其它机器人陷阱。

    对于某些有偿提交的查寻引擎(像雅虎),支付象征性费用提交也许会节省一些时间,虽然雅虎有偿提交方案不保证提交人/公司包括在他们的查寻结果中。

    [编辑] 白帽法

    搜寻引擎优化的白帽法包括遵循搜寻引擎哪些可接受哪些不能接受的指导方针。他们的建议一般是为用户创造内容,而非搜寻引擎、是让这些内容易于被蜘蛛机器人索引、并且不尝试对搜寻引擎系统耍花招。经常网站员于设计或构建他们的网站犯了致命错误、疏忽地"毒害" 该站以致排名不会很好。白帽法优化员企图发现并纠正错误,譬如机器无法读取的选单、无效链接、临时改变导向、或粗劣的导引结构。

    因为搜寻引擎是以文本为中心,许多有助于网页亲和力的同样手段同样便利于搜寻引擎优化。这些方法包括最佳化图形内容、包括ALT 属性、和增加文本说明。甚至Flash动画可于设计该页时包括替代性内容──这本来用来给访客无法阅读Flash的环境用的──来帮助优化。

    这里是一些搜寻引擎认为适当的方法:

    • 在每页使用一个短、独特、和相关的标题。
    • 编辑网页,用与该页的主题有关的具体术语替换隐晦的字眼。这有助于该站诉求的观众群,在搜寻引擎上搜寻而被正确导引至该站。
    • 在该站点增加相当数量的原创内容。
    • 使用合理大小、准确描述的汇标,而不过度使用关键字、惊叹号、或不相关标题术语。
    • 确认所有页可透过正常的链结来访问,而非只能透过JavaJavaScriptMacromedia Flash应用程序访问。这可透过使用一个专属列出该站所有内容的网页达成(网站地图(SiteMap))
    • 透过自然方式开发链结:Google不花功夫在这有点混淆不清的指南上。写封电子邮件给网站员,告诉他您刚刚贴了一篇挺好的文章,并且请求链接,这种做法大概很可能为搜寻引擎所认可。
    • 参与其他网站的网络集团(译按:web ring 指的是有相同主题的结盟站群)──只要其它网站是独立的、分享同样题目、和可比较的品质。

    [编辑] 黑帽法

    主条目:垃圾索引

    垃圾索引(Spamdexing)意指透过欺骗技术和滥用搜索算法来推销毫不相关、主要以商业为著眼的网页。许多搜索引擎管理员认为任何搜索引擎优化的形式,其目的用来改进网站的页排名者,都是垃圾索引。然而,随时间流逝,业界内公众舆论发展出哪些是哪些不是可接受的、促进某站的搜索引擎排名与流量结果的手段。

    因为搜寻引擎以高度自动化的方式运作,网站员通常可以利用某些未被搜寻引擎认可的手段、方法来促进排名。这些方法经常未被注意除非搜索引擎雇员亲临该站点并注意到不寻常活动、或在排名算法上的某个小变化导致站点丢失以过去方式取得的高排名。有时某些公司雇用优化顾问评估竞争者的站点、和"不道德的" 优化方法向搜寻引擎报告。

    垃圾索引经常与合法的、不介入欺骗的搜寻引擎优化技术搞混。垃圾索引专注于让该网站得到更多的曝光率,而非他们选用的关键词,这导致令人不满的搜寻结果;相对的,优化则专注于他们希望得到的排名,基于特定目标关键字上;这导致令人满意的搜寻体验。

    当这些不好的垃圾索引被发现时, 搜寻引擎也许会对那些被发现使用不道德的优化手段者采取行动。在2006 年2月,Google拿掉了BMW 德国站和Ricoh 德国站,因为他们用了这些方法[7]

    [编辑] 搜寻引擎优化与行销

    当这篇文章倾向建立间的搜寻引擎优化师间戴哪种颜色帽子的区别,这些产业写照实际上对于某些促成业界某些影响巨大成就部分的工作者著墨不多。

    有相当多的优化实践者只是认为把搜寻引擎当作该站的另一个访客,并设法让该站点亲和如同真正访客拜访那些网页一般。他们的工作焦点不集中于将许多术语于搜寻引擎排名最高,而是帮助站点拥有者达到该站点的商业目标。这也许以实现导引基本搜寻结果、甚或利用在搜寻引擎做有偿广告的形式到某些页。至于其他页,建立高品质网页吸引参与和说服, 阐明技术问题,这些手段可让搜寻引擎继续爬过并检索这些站。另,设定分析程序可以让网站主衡量该网站成就,并且让该站更亲和更有用。

    这些搜寻引擎优化员可能是某组织的一员,或者是个顾问,而搜索引擎优化也许只是他们每天例行工作的一部分。通常他们有关搜寻引擎功能的知识来自于论坛、部落格、一般的会议和研讨会间的互动和讨论主题、甚或经由在他们自己的站点实验获得。目前极少的大学课程涵盖电子交易观点的网上行销,可能肇因于网络上一日数变的关系。

    当许多人检阅和致力于符合搜寻引擎指南时──这可帮助某人于网络上的成功打造坚实基础──跟随这些指南的结果实际上不过只是个开始。许多人认为搜索引擎行销不过是个搜寻优化下较大一点的支部而已,但它可能是许多主要集中于搜寻引擎优化的老兵,集合了更多更多的行销灵感所带来的成就。搜寻引擎因为认知到这一点,他们扩展了搜寻引擎覆盖面,包括了RSS提供元、录影搜寻、地方结果、地图、和更多他们必须提供的功能。

    [编辑] 法律案例

    2002 年, 搜索引擎操作者SearchKing在俄克拉何马法院提出了诉讼,反对搜索引擎Google。SearchKing的要求是, Google防止垃圾索引的手段构成了一条不公正的商业惯例。这也许可想像为垃圾电子邮件营销者控告反垃圾电子邮件者,如同许多反对地图以及其他DNS黑洞表(DNSBL)的案例。2003年1月,法院发布了一个Google胜诉的判决摘要[8]

    [编辑] 高品质网站通常排名很优

    对许多有心于最大化网站附加价值的网站员们,可阅读由搜寻引擎出版的、以及W3C发行的编码指南。如果该指南被遵循,站点频繁地更新,有用、原创的内容,和建立几个实用、有意义的导入链接,获得相当可观数目的基本搜寻流量不是甚么难事。

    当站点拥有有用的内容,其它站点员自然而然会连结至该站,进而增加访客它的网页级别和访客流。当访客发现一个有用的网站,他们倾向于利用电子邮件或者及时讯息连结介绍给其它访客。

    总括来说,增进网站品质的搜寻引擎优化实现很可能比直接寻找操控搜寻排名手段的短期实现要活得长久。顶尖的搜寻引擎优化员们找寻的目标与搜寻引擎追求发扬光大的东西二者不雷而同。他们是:相关性、对他们用户有用的内容。