随笔 - 18  文章 - 0 评论 - 43 trackbacks - 1

posted @ 2009-02-21 00:23 雪山飞狐12345 阅读(2005) 评论(16) 编辑

 网络神采/狂采/火车头全功能,C#全部代码打包转让;

 

1、 多线程多任务采集器

a)         起始地址支持普通url地址、url序列递增、动态post序列url

b)         树形导航过滤url路径,更快更精准;

c)         支持中间页采集数据,比如有些点击数、回复数、论坛版块名称的抽取;

d)         中间页使用简易脚本方式进行采集,配置极其容易,同时辅助向导帮忙确认配置是否正确;

e)         数据抽取支持传统字符串前、后缀数据抽取,高级的正则表达式抽取,最最亮眼的是“可视化的智能采集向导”,使用xpathhtml结点进行高效、精准定位,最大限度降低了使用门槛;

f)          数据保存支持多终端,支持的终端包括:excel/access,mssql,mysql等数据库,同时支持数据直接写lucene索引,高效生成搜索引擎所需要的索引文件;

2、 数据发布模块

a)         支持像任何cms后台直接发布数据;已经内置了市场上知名的论坛,像discuz,phpwind,动网论坛等等,还有各类的cms;

b)         用户自行配置发布接口也比相关软件简单,我们采用模拟浏览器点击的方法对数据进行自动发布;

3、 倒排索引全部代码

a)         内置lucene最新版本的所有代码;

b)         高效的中文分词模块所有代码;

c)         Lucene索引管理器C#所有代码,让您像管理数据库一样管理您的lucene索引文件;

d)         多线程、多路径lucene检索,支持检索语法、多域,模糊等各种形式;让lucene成为您海量数据访问的存储结构;

4、 辅助模块

a)         日志系统

b)         信息传递系统

c)         各种设计模式封装

5、 这些代码意味着什么?

a)         最短的时间,最小的成本,可以开张一家公司?

b)         做什么?采集器、舆情监控系统,口碑营销后台,广告效果监控,网络信息雷达,垂直搜索引擎,站内搜索引擎……

c)         代码的质量?作者有国内知名公司工作开发经验,多年的心血,历经多个版本改良,早已达到商用标准?商用是什么标准?一天采集千w条数据,连续一周软件稳定运行;1亿条数据检索响应时间为0.几秒……

d)         代码的可维护性:严格基于设计模式封装,mvc三层结构,对于升级开放,对于变化封闭;

6、 联系我:

QQ:86820609

建议企业联系,可当面交易或支付宝,价格面议。

 

 

posted @ 2010-10-09 18:08 雪山飞狐12345 阅读(528) 评论(3) 编辑

三、排序过滤和分页
      仅仅把东西搜出来是不够的,好的检索工具还应当能够对检索的结果进行排序,优先将最相关的内容送出
  或是按照某种规则,将检索结果送出。
  1.文档得分规则
      文档得分主要是由4部分内容来决定,即tf(词条频率)、idf(反转文档频率)、boost(Field的激励因子)
  和lengthNorm(长度因子)
      tf:某个关键字在某文档中出现次数的平方根
      idf:Math.log(numDocs/docFreq+1)+1.0   (numDocs:表示索引中总共的文档数量,docFreq:当前检索
          的关键字的文档总数)
      lengthNorm:在lucene的底层实现中,lengthNorm是一种固定值,它无需在索引时指定,只需要跟随创建
          索引过程,被写入索引中。
      boost:文档的boost值一般情况下默认为1.0,在建立索引时可通过预先人为的指定Document或Field的
          boost值,来改变搜索时文档的得分,从而改变文档在搜索结果中的排序位置。
  2.使用Sort排序
       该类有6种构造函数
       public Sort()
       public Sort(String field) 默认为降序
       public Sort(String field,boolean reverse)
       public Sort(String[] fields)
       public Sort(SortField field)
       public Sort(SortField[] fields)
       Sort可以对单一的Field进行排序,但是对多个Field的排序就必须要借助SortField类了。SortField类
    是个包装类,它能描述Field和reverse(排序)信息,在搜索结果中有很多记录时,应该正确的指定Field
    的类型,这样对排序过程的效率会有很大的提高。
       排序有多种
       a.按文档得分进行排序:这是lucene默认的排序方式
             如:Hits hist=searcher.search(q,Sort.RELEVANCE);   Sort.RELEVANCE表示当前的排序法则
         是按照文档的得分进行降序排列
       b.按文档的内部ID号来排序:在建立索引的时候,lucene会为每个文档建立一个内部的ID号
             如:Hits hits=searcher.search(q,Sort.INDEXORDER)   Sort.INDEXORDER表示排序法则是按
         照文档的内部ID号来排序
       c.按一个或多个Field来排序:把每个Field封装成SortField,然后以SortField[]数组的形式传入Sort类
       注意:比较字符串时,Locale信息的不同,很有可能会影响到比较的结果,因此SortField类有这样的构
       造函数,可以帮助指定Locale信息
          public SortField(String field,Locale locale)
          public SortField(String field,Locale locale,boolean reverse)
       当指定Locale后,lucene才可以正确的对字符串进行比较,从而正确的排序。不过大多数情况下,使用Sort
       进行排序时,排序的内容多为int型或是日期型。即便需要进行字符串的比较,也最好使用仅包含ASCII的
       字符串Field进行,这样可以确保排序的正确,同时,尽量提高排序的效率。
  3.搜索时使用过滤器
    简单示例:

Java代码 复制代码

  1. public class MySecurityFilter extends Filter{   
  2. public static final int SECURITY=0;   
  3. public BitSet bits(IndexReader reader) throws IOException{   
  4. //先初始化一个BitSet对象 
  5. final BitSet bits=new BitSet(reader.maxDoc());   
  6. //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
  7.        bits.set(0,bits.size()-1);   
  8. //构造一个Term对象,代表最高安全级别
  9.        Term term=new Term("securitylevel",SECURITY+"");   
  10. //从索引中取出具有最高安全级别的文档
  11.        TermDocs termDocs=reader.termDocs(term);   
  12. //遍历每个文档
  13. while(termDocs.next()){   
  14.           bits.set(termDocs.doc(),false);   
  15.        }   
  16.       retrun bits;   
  17.     }   
  18. }  
        public class MySecurityFilter extends Filter{
            public static final int SECURITY=0;
            public BitSet bits(IndexReader reader) throws IOException{
               //先初始化一个BitSet对象 
               final BitSet bits=new BitSet(reader.maxDoc());
               //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
               bits.set(0,bits.size()-1);
               //构造一个Term对象,代表最高安全级别
               Term term=new Term("securitylevel",SECURITY+"");
               //从索引中取出具有最高安全级别的文档
               TermDocs termDocs=reader.termDocs(term);
               //遍历每个文档
               while(termDocs.next()){
                  bits.set(termDocs.doc(),false);
               }
              retrun bits;
            }
        }
      

        或者:

Java代码 复制代码

  1. public class MySecurityFilter extends Filter{   
  2. public static final int SECURITY=0;   
  3. public BitSet bits(IndexReader reader) throws IOException{   
  4. //先初始化一个BitSet对象 
  5. final BitSet bits=new BitSet(reader.maxDoc());   
  6. //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
  7.        bits.set(0,bits.size()-1);   
  8. //构造一个Term对象,代表最高安全级别
  9.        Term term=new Term("securitylevel",SECURITY+"");   
  10. //初始化一个IndexSearcher对象
  11. //查找securitylevel这个Field的值为SECURITY的文档
  12.        IndexSearcher searcher=new IndexSearcher(term);   
  13.        Hits hits=searcher.search(new TermQuery(term));   
  14. for(int i=0;i<hits.length();i++){   
  15.           bits.set(hits.id(i),false);   
  16.        }   
  17.       retrun bits;   
  18.     }   
  19. }  
        public class MySecurityFilter extends Filter{
            public static final int SECURITY=0;
            public BitSet bits(IndexReader reader) throws IOException{
               //先初始化一个BitSet对象 
               final BitSet bits=new BitSet(reader.maxDoc());
               //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
               bits.set(0,bits.size()-1);
               //构造一个Term对象,代表最高安全级别
               Term term=new Term("securitylevel",SECURITY+"");
               //初始化一个IndexSearcher对象
               //查找securitylevel这个Field的值为SECURITY的文档
               IndexSearcher searcher=new IndexSearcher(term);
               Hits hits=searcher.search(new TermQuery(term));
               for(int i=0;i<hits.length();i++){
                  bits.set(hits.id(i),false);
               }
              retrun bits;
            }
        }
       

        注意:Filter实际上在进行真正的查询之前,已经遍历过一次一遍索引了,因此无
        论使用何种Filter,都不应该忽视它对查询效率的影响。
   4.在结果中查询(QueryFilter)
          在QueryFilter的bits方法中,调用IndexSearcher方法,对注入的Query进行一次
     检索,然后在要被返回的BitSet中,将检索结果所对应的文档标记为true,即过滤掉了
     所有不再检索结果范围内的文档。
posted @ 2010-09-01 13:49 雪山飞狐12345 阅读(443) 评论(0) 编辑

Lucene所支持的查询语法可见http://lucene.apache.org/java/3_0_1/queryparsersyntax.html

(1) 语法关键字

+ - && || ! ( ) { } [ ] ^ " ~ * ? : \

如果所要查询的查询词中本身包含关键字,则需要用\进行转义

(2) 查询词(Term)

Lucene支持两种查询词,一种是单一查询词,如"hello",一种是词组(phrase),如"hello world"。

(3) 查询域(Field)

在查询语句中,可以指定从哪个域中寻找查询词,如果不指定,则从默认域中查找。

查询域和查询词之间用:分隔,如title:"Do it right"。

:仅对紧跟其后的查询词起作用,如果title:Do it right,则仅表示在title中查询Do,而it right要在默认域中查询。

(4) 通配符查询(Wildcard)

支持两种通配符:?表示一个字符,*表示多个字符。

通配符可以出现在查询词的中间或者末尾,如te?t,test*,te*t,但决不能出现在开始,如*test,?test。

(5) 模糊查询(Fuzzy)

模糊查询的算法是基于Levenshtein Distance,也即当两个词的差别小于某个比例的时候,就算匹配,如roam~0.8,即表示差别小于0.2,相似度大于0.8才算匹配。

(6) 临近查询(Proximity)

在词组后面跟随~10,表示词组中的多个词之间的距离之和不超过10,则满足查询。

所谓词之间的距离,即查询词组中词为满足和目标词组相同的最小移动次数。

如索引中有词组"apple boy cat"。

如果查询词为"apple boy cat"~0,则匹配。

如果查询词为"boy apple cat"~2,距离设为2方能匹配,设为1则不能匹配。

(0)

boy

apple

cat

(1)

boy

apple

cat

(2)

apple

boy

cat

如果查询词为"cat boy apple"~4,距离设为4方能匹配。

(0)

cat

boy

apple

(1)

cat

boy

apple

(2)

boy

cat

apple

(3)

boy

apple

cat

(4)

apple

boy

cat

(7) 区间查询(Range)

区间查询包含两种,一种是包含边界,用[A TO B]指定,一种是不包含边界,用{A TO B}指定。

如date:[20020101 TO 20030101],当然区间查询不仅仅用于时间,如title:{Aida TO Carmen}

(8) 增加一个查询词的权重(Boost)

可以在查询词后面加^N来设定此查询词的权重,默认是1,如果N大于1,则说明此查询词更重要,如果N小于1,则说明此查询词更不重要。

如jakarta^4 apache,"jakarta apache"^4 "Apache Lucene"

(9) 布尔操作符

布尔操作符包括连接符,如AND,OR,和修饰符,如NOT,+,-。

默认状态下,空格被认为是OR的关系,QueryParser.setDefaultOperator(Operator.AND)设置为空格为AND。

+表示一个查询语句是必须满足的(required),NOT和-表示一个查询语句是不能满足的(prohibited)。

(10) 组合

可以用括号,将查询语句进行组合,从而设定优先级。

如(jakarta OR apache) AND website

Lucene的查询语法是由QueryParser来进行解析,从而生成查询对象的。

通过编译原理我们知道,解析一个语法表达式,需要经过词法分析和语法分析的过程,也即需要词法分析器和语法分析器。

QueryParser是通过JavaCC来生成词法分析器和语法分析器的。

二、JavaCC介绍

本节例子基本出于JavaCC tutorial的文章,http://www.engr.mun.ca/~theo/JavaCC-Tutorial/

JavaCC是一个词法分析器和语法分析器的生成器。

所谓词法分析器就是将一系列字符分成一个个的Token,并标记Token的分类。

例如,对于下面的C语言程序:

int main() {

    return 0 ;

}

将被分成以下的Token:

“int”, “ ”, “main”, “(”, “)”,

“”,“{”, “\n”, “\t”, “return”

“”,“0”,“”,“;”,“\n”,

“}”, “\n”, “”

标记了Token的类型后如下:

KWINT, SPACE, ID, OPAR, CPAR,

SPACE, OBRACE, SPACE, SPACE, KWRETURN,

SPACE, OCTALCONST, SPACE, SEMICOLON, SPACE,

CBRACE, SPACE, EOF

EOF表示文件的结束。

词法分析器工作过程如图所示:

clip_image002

此一系列Token将被传给语法分析器(当然并不是所有的Token都会传给语法分析器,本例中SPACE就例外),从而形成一棵语法分析树来表示程序的结构。

clip_image004

JavaCC本身既不是一个词法分析器,也不是一个语法分析器,而是根据指定的规则生成两者的生成器。

2.1、第一个实例——正整数相加

下面我们来看第一个例子,即能够解析正整数相加的表达式,例如99+42+0+15。

(1) 生成一个adder.jj文件

此文件中写入的即生成词法分析器和语法分析器的规则。

(2) 设定选项,并声明类

/* adder.jj Adding up numbers */

options {

  STATIC = false ;

}

PARSER_BEGIN(Adder)

class Adder {

  static void main( String[] args ) throws ParseException, TokenMgrError {

    Adder parser = new Adder( System.in ) ;

    parser.Start() ;

  }

}

PARSER_END(Adder)

STATIC选项默认是true,设为false,使得生成的函数不是static的。

PARSER_BEGIN和PARSER_END之间的java代码部分,此部分不需要通过JavaCC根据规则生成java代码,而是直接拷贝到生成的java代码中的。

(3) 声明一个词法分析器

SKIP : { " " }

SKIP : { "\n" | "\r" | "\r\n" }

TOKEN : { < PLUS : "+" > }

TOKEN : { < NUMBER : (["0"-"9"])+ > }

第一二行表示空格和回车换行是不会传给语法分析器的。

第三行声明了一个Token,名称为PLUS,符号为“+”。

第四行声明了一个Token,名称为NUMBER,符号位一个或多个0-9的数的组合。

如果词法分析器分析的表达式如下:

(4) 声明一个语法分析器

void Start() :

{}

{

  <NUMBER>

  (

    <PLUS>

    <NUMBER>

  )*

  <EOF>

}

语法分析器使用BNF表达式。

上述声明将生成start函数,称为Adder类的一个成员函数

语法分析器要求输入的语句必须以NUMBER开始,以EOF结尾,中间是零到多个PLUS和NUMBER的组合。

(5) 用javacc编译adder.jj来生成语法分析器和词法分析器

最后生成的adder.jj如下:

options
{
  static = false;
}

PARSER_BEGIN(Adder)
package org.apache.javacc;

public class Adder
{
  public static void main(String args []) throws ParseException
  {
    Adder parser = new Adder(System.in);
    parser.start();
  }
}
PARSER_END(Adder)

SKIP :
{
  " "
| "\r"
| "\t"
| "\n"
}

TOKEN : /* OPERATORS */
{
  < PLUS : "+" >
}

TOKEN :
{
  < NUMBER : ([ "0"-"9" ])+ >
}

void start() :
{}
{
  <NUMBER>
  (
    <PLUS>
    <NUMBER>
  )*
}

用JavaCC编译adder.jj生成如下文件:

下面我们对adder.jj生成的start函数进行分析:

final public void start() throws ParseException {

//从词法分析器取得下一个Token,而且要求必须是NUMBER类型,否则抛出异常。

//此步要求表达式第一个出现的字符必须是NUMBER。

  jj_consume_token(NUMBER);

  label_1:

  while (true) {

//jj_ntk()是取得下一个Token的类型,如果是PLUS,则继续进行,如果是EOF则退出循环。

    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {

    case PLUS:

      ;

      break;

    default:

      jj_la1[0] = jj_gen;

      break label_1;

    }

//要求下一个PLUS字符,再下一个是一个NUMBER,如此下去。

    jj_consume_token(PLUS);

    jj_consume_token(NUMBER);

  }

}

(6) 运行Adder.java

如果输入“123+456”则不报任何错误。

如果输入“123++456”则报如下异常:

Exception in thread "main" org.apache.javacc.ParseException: Encountered " "+" "+ "" at line 1, column 5.
Was expecting:
    <NUMBER> ...
    at org.apache.javacc.Adder.generateParseException(Adder.java:185)
    at org.apache.javacc.Adder.jj_consume_token(Adder.java:123)
    at org.apache.javacc.Adder.start(Adder.java:24)
    at org.apache.javacc.Adder.main(Adder.java:8)

如果输入“123-456”则报如下异常:

Exception in thread "main" org.apache.javacc.TokenMgrError: Lexical error at line 1, column 4.  Encountered: "-" (45), after : ""
    at org.apache.javacc.AdderTokenManager.getNextToken(AdderTokenManager.java:262)
    at org.apache.javacc.Adder.jj_ntk(Adder.java:148)
    at org.apache.javacc.Adder.start(Adder.java:15)
    at org.apache.javacc.Adder.main(Adder.java:8)

2.2、扩展语法分析器

在上面的例子中的start函数中,我们仅仅通过语法分析器来判断输入的语句是否正确。

我们可以扩展BNF表达式,加入Java代码,使得经过语法分析后,得到我们想要的结果或者对象。

我们将start函数改写为:

int start() throws NumberFormatException :

{

//start函数中有三个变量

  Token t ;

  int i ;

  int value ;

}

{

//首先要求表达式的第一个一定是一个NUMBER,并把其值付给t

  t= <NUMBER>

//将t的值取出来,解析为整型,放入变量i中

  { i = Integer.parseInt( t.image ) ; }

//最后的结果value设为i

  { value = i ; }

//紧接着应该是零个或者多个PLUS和NUMBER的组合

  (

    <PLUS>

//每出现一个NUMBER,都将其付给t,并将t的值解析为整型,付给i

    t= <NUMBER>

    { i = Integer.parseInt( t.image ) ; }

//将i加到value上

    { value += i ; }

  )*

//最后的value就是表达式的和

  { return value ; }

}

生成的start函数如下:

final public int start() throws ParseException, NumberFormatException {

  Token t;

  int i;

  int value;

  t = jj_consume_token(NUMBER);

  i = Integer.parseInt(t.image);

  value = i;

  label_1: while (true) {

    switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {

    case PLUS:

      ;

      break;

    default:

      jj_la1[0] = jj_gen;

      break label_1;

    }

    jj_consume_token(PLUS);

    t = jj_consume_token(NUMBER);

    i = Integer.parseInt(t.image);

    value += i;

  }

  {

    if (true)

      return value;

  }

  throw new Error("Missing return statement in function");

}

从上面的例子,我们发现,把一个NUMBER取出,并解析为整型这一步是可以共用的,所以可以抽象为一个函数:

int start() throws NumberFormatException :

{

  int i;

  int value ;

}

{

  value = getNextNumberValue()

  (

    <PLUS>

    i = getNextNumberValue()

    { value += i ; }

  )*

  { return value ; }

}

int getNextNumberValue() throws NumberFormatException :

{

  Token t ;

}

{

  t=<NUMBER>

  { return Integer.parseInt( t.image ) ; }

}

生成的函数如下:

final public int start() throws ParseException, NumberFormatException {

  int i;

  int value;

  value = getNextNumberValue();

  label_1: while (true) {

    switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {

    case PLUS:

      ;

      break;

    default:

      jj_la1[0] = jj_gen;

      break label_1;

    }

    jj_consume_token(PLUS);

    i = getNextNumberValue();

    value += i;

  }

  {

    if (true)

      return value;

  }

  throw new Error("Missing return statement in function");

}

final public int getNextNumberValue() throws ParseException, NumberFormatException {

  Token t;

  t = jj_consume_token(NUMBER);

  {

    if (true)

      return Integer.parseInt(t.image);

  }

  throw new Error("Missing return statement in function");

}

2.3、第二个实例:计算器

(1) 生成一个calculator.jj文件

用于写入生成计算器词法分析器和语法分析器的规则。

(2) 设定选项,并声明类

options {

STATIC = false ;

}

PARSER_BEGIN(Calculator)

  import java.io.PrintStream ;

  class Calculator {

    static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException {

      Calculator parser = new Calculator( System.in ) ;

      parser.Start( System.out ) ;

    }

    double previousValue = 0.0 ;

  }

PARSER_END(Calculator)

previousValue用来记录上一次计算的结果。

(3) 声明一个词法分析器

SKIP : { " " }

TOKEN : { < EOL:"\n" | "\r" | "\r\n" > }

TOKEN : { < PLUS : "+" > }

我们想要支持小数,则有四种情况:没有小数,小数点在中间,小数点在前面,小数点在后面。则语法规则如下:

TOKEN { < NUMBER : (["0"-"9"])+ | (["0"-"9"])+ "." (["0"-"9"])+ | (["0"-"9"])+ "." | "." (["0"-"9"])+ > }

由于同一个表达式["0"-"9"]使用了多次,因而我们可以定义变量,如下:

TOKEN : { < NUMBER : <DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "." <DIGITS>> }

TOKEN : { < #DIGITS : (["0"-"9"])+ > }

(4) 声明一个语法分析器

我们想做的计算器包含多行,每行都是一个四则运算表达式,语法规则如下:

Start -> (Expression EOL)* EOF

void Start(PrintStream printStream) throws NumberFormatException :

{}

{

  (

    previousValue = Expression()

    <EOL>

    { printStream.println( previousValue ) ; }

  )*

  <EOF>

}

每一行的四则运算表达式如果只包含加法,则语法规则如下:

Expression -> Primary (PLUS Primary)*

double Expression() throws NumberFormatException :

{

  double i ;

  double value ;

}

{

  value = Primary()

  (

    <PLUS>

    i= Primary()

    { value += i ; }

  )*

  { return value ; }

}

其中Primary()得到一个数的值:

double Primary() throws NumberFormatException :

{

  Token t ;

}

{

  t= <NUMBER>

  { return Double.parseDouble( t.image ) ; }

}

(5) 扩展词法分析器和语法分析器

如果我们想支持减法,则需要在词法分析器中添加:

TOKEN : { < MINUS : "-" > }

语法分析器应该变为:

Expression -> Primary (PLUS Primary | MINUS Primary)*

double Expression() throws NumberFormatException :

{

  double i ;

  double value ;

}

{

  value = Primary()

  (

    <PLUS>

    i = Primary()

    { value += i ; }

    |

    <MINUS>

    i = Primary()

    { value -= i ; }

  )*

  { return value ; }

}

如果我们想添加乘法和除法,则在词法分析器中应该加入:

TOKEN : { < TIMES : "*" > }

TOKEN : { < DIVIDE : "/" > }

对于加减乘除混合运算,则应该考虑优先级,乘除的优先级高于加减,应该先做乘除,再做加减:

Expression -> Term (PLUSTerm | MINUSTerm)*

Term -> Primary (TIMES Primary | DIVIDE Primary)*

double Expression() throws NumberFormatException :

{

  double i ;

  double value ;

}

{

  value = Term()

  (

    <PLUS>

    i= Term()

    { value += i ; }

    |

    <MINUS>

    i= Term()

    { value -= i ; }

  )*

  { return value ; }

}

double Term() throws NumberFormatException :

{

  double i ;

  double value ;

}

{

  value = Primary()

  (

    <TIMES>

    i = Primary()

    { value *= i ; }

    |

    <DIVIDE>

    i = Primary()

    { value /= i ; }

  )*

  { return value ; }

}

下面我们要开始支持括号,负号,以及取得上一行四则运算表达式的值。

对于词法分析器,我们添加如下Token:

TOKEN : { < OPEN PAR : "(" > }

TOKEN : { < CLOSE PAR : ")" > }

TOKEN : { < PREVIOUS : "$" > }

对于语法分析器,对于最基本的表达式,有四种情况:

其可以是一个NUMBER,也可以是上一行四则运算表达式的值PREVIOUS,也可以是被括号括起来的一个子语法表达式,也可以是取负的一个基本语法表达式。

Primary –> NUMBER | PREVIOUS | OPEN_PAR Expression CLOSE_PAR | MINUS Primary

double Primary() throws NumberFormatException :

{

  Token t ;

  double d ;

}

{

  t=<NUMBER>

  { return Double.parseDouble( t.image ) ; }

  |

  <PREVIOUS>

  { return previousValue ; }

  |

  <OPEN PAR> d=Expression() <CLOSE PAR>

  { return d ; }

  |

  <MINUS> d=Primary()

  { return -d ; }

}

(6) 用javacc编译calculator.jj来生成语法分析器和词法分析器

最后生成的calculator.jj如下:

options
{
  static = false;
}

PARSER_BEGIN(Calculator)
package org.apache.javacc.calculater;
  import java.io.PrintStream ;
  class Calculator {
    static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException {
      Calculator parser = new Calculator( System.in ) ;
      parser.start( System.out ) ;
    }
    double previousValue = 0.0 ;
  }
PARSER_END(Calculator)

SKIP : { " " }
TOKEN : { < EOL: "\n" | "\r" | "\r\n" > }
TOKEN : { < PLUS : "+" > }
TOKEN : { < MINUS : "-" > }
TOKEN : { < TIMES : "*" > }
TOKEN : { < DIVIDE : "/" > }
TOKEN : { < NUMBER : <DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "." <DIGITS>> }
TOKEN : { < #DIGITS : (["0"-"9"])+ > }
TOKEN : { < OPEN_PAR : "(" > }
TOKEN : { < CLOSE_PAR : ")" > }
TOKEN : { < PREVIOUS : "$" > }

void start(PrintStream printStream) throws NumberFormatException :
{}
{
  (
    previousValue = Expression()
    { printStream.println( previousValue ) ; }
  )*
}

double Expression() throws NumberFormatException :
{
  double i ;
  double value ;
}
{
  value = Term()
  (
    <PLUS>
    i= Term()
    { value += i ; }
    |
    <MINUS>
    i= Term()
    { value -= i ; }
  )*
  { return value ; }
}

double Term() throws NumberFormatException :
{
  double i ;
  double value ;
}
{
  value = Primary()
  (
    <TIMES>
    i = Primary()
    { value *= i ; }
    |
    <DIVIDE>
    i = Primary()
    { value /= i ; }
  )*
  { return value ; }
}

double Primary() throws NumberFormatException :
{
  Token t ;
  double d ;
}
{
  t=<NUMBER>
  { return Double.parseDouble( t.image ) ; }
  |
  <PREVIOUS>
  { return previousValue ; }
  |
  <OPEN_PAR> d=Expression() <CLOSE_PAR>
  { return d ; }
  |
  <MINUS> d=Primary()
  { return -d ; }
}

生成的start函数如下:

final public void start(PrintStream printStream) throws ParseException, NumberFormatException {

  label_1:

  while (true) {

    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {

    case MINUS:

    case NUMBER:

    case OPEN_PAR:

    case PREVIOUS:

      ;

      break;

    default:

      jj_la1[0] = jj_gen;

      break label_1;

    }

    previousValue = Expression();

    printStream.println( previousValue ) ;

  }

}

final public double Expression() throws ParseException, NumberFormatException {

  double i ;

  double value ;

  value = Term();

  label_2:

  while (true) {

    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {

    case PLUS:

    case MINUS:

      ;

      break;

    default:

      jj_la1[1] = jj_gen;

      break label_2;

    }

    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {

    case PLUS:

      jj_consume_token(PLUS);

      i = Term();

      value += i ;

      break;

    case MINUS:

      jj_consume_token(MINUS);

      i = Term();

      value -= i ;

      break;

    default:

      jj_la1[2] = jj_gen;

      jj_consume_token(-1);

      throw new ParseException();

    }

  }

  {if (true) return value ;}

  throw new Error("Missing return statement in function");

}

final public double Term() throws ParseException, NumberFormatException {

  double i ;

  double value ;

  value = Primary();

  label_3:

  while (true) {

    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {

    case TIMES:

    case DIVIDE:

      ;

      break;

    default:

      jj_la1[3] = jj_gen;

      break label_3;

    }

    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {

    case TIMES:

      jj_consume_token(TIMES);

      i = Primary();

      value *= i ;

      break;

    case DIVIDE:

      jj_consume_token(DIVIDE);

      i = Primary();

      value /= i ;

      break;

    default:

      jj_la1[4] = jj_gen;

      jj_consume_token(-1);

      throw new ParseException();

    }

  }

  {if (true) return value ;}

  throw new Error("Missing return statement in function");

}

final public double Primary() throws ParseException, NumberFormatException {

  Token t ;

  double d ;

  switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {

  case NUMBER:

    t = jj_consume_token(NUMBER);

    {if (true) return Double.parseDouble( t.image ) ;}

    break;

  case PREVIOUS:

    jj_consume_token(PREVIOUS);

    {if (true) return previousValue ;}

    break;

  case OPEN_PAR:

    jj_consume_token(OPEN_PAR);

    d = Expression();

    jj_consume_token(CLOSE_PAR);

    {if (true) return d ;}

    break;

  case MINUS:

    jj_consume_token(MINUS);

    d = Primary();

    {if (true) return -d ;}

    break;

  default:

    jj_la1[5] = jj_gen;

    jj_consume_token(-1);

    throw new ParseException();

  }

  throw new Error("Missing return statement in function");

}

posted @ 2010-09-01 12:13 雪山飞狐12345 阅读(498) 评论(0) 编辑

IndexSearcher 构造函数

IndexSearcher继承于Searcher类,有四种构造函数,但最终以IndexReader作为实际的索引目录读取器
  Searcher searcher=new IndexSearcher(String indexDir);

Searcher searcher=new IndexSearcher(Directory directory)

Searcher searcher=new IndexSearcher(IndexReader indexreader)

Searcher searcher=new IndexSeacher(IndexReader indexreader ,boolean closeReader)

IndexSeacher.search()重载函数
search(Query query) Hits
search(Query query ,Filter filter) Hits
search(Query query ,Filter filter ,Sort sort) Hits
search(Query query ,Sort sort) Hits

search(Query query ,Filter filter ,HitCollector hitcollector) Hits

search(Query query ,Filter filter ,int arg) Hits

search(Query query ,Filter filter ,int arg ,Sort sort) Hits

search(Query query ,HitCollector hitcollector) Hits

用来完成对索检结果的排序、过虑等功能而设计的

构建各种Query
  用户输入一个关键字,应该分析判断构建哪种Query进行索检,这样才能提高效率,同时得出更加有效的结果

(1)词条搜索TermQuery
   TermQuery是最简单,最常用的Query。TermQuery可以理解为”词条搜索”,其实是一个名/值对照,名是Field(字段)而值则表示字段中所包含的某个关键字
使用TermQuery进行搜索 首先构造一个Term对象
    Term term=new Term(“field”,”keyword”);

然后用Term对象为参数构造一个TermQuery对象
Query query=new TermQuery(term);

(2)“与或非”搜索 BooleanQuery
    BooleanQuery继承自Query类,是实际开发中经常使用的一种Query,它其实一个组合的Query,由于查询是可嵌套的如果子句太多可能会导致查询效率降低,lucene默认的子句不能超过1024个
添加子句的API接口
    public void add(Query query ,boolean required ,boolean prohibited);

             True &False 当前加入的子句必须要满足   (与
False &False 当前加入的子句可选的       (或
False &True 当前加入的子句不可以被满足 (非

实例:

   IndexSeacher  searcher=new IndexSearch(“c://temp”);

   Query query1=TermQuery(new Term(“field”,”keyword”));

Query query2=TermQuery(new Term(“field”,”keyword”));

   Query query=BooleanQuery();

   query.add(query1,true,false);

   query.add(query2,true,false);

   Hits hits=searcher.search(query);

(3)RangeQuery范围搜索
   RangeQuery继承自Query类,表示在某个范围内的搜索条件,在查询“开始词条“和”结束词条“可以被包含在内也可以不包含在内,比如查询时间,数字,字母等在一个段内的文档,只有一个构造函数
      public RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive)

Inclusive表示是否包含边界条件本身

(4)PrefixQuery前缀搜索

PrefixQuery继承Query类,可以进行前缀查找,只有一个构造函数
      public PrefixQuery(Term prefix)

(5)PhraseQuery多关键字搜索
     除了普通的TermQuery外,Lucene还提供了一种Phrase查询功能。用户常常查找的并非是一个简单的单词,很有可能是几个不同的关键词,这些关键词之间要么联系紧密,成为一个精确的短语,或者这几个关键词之间还穿插了其他的无关的关键字。这些关键字之间拥有与查找无关的短语,使他们文档的分值一般会比较低
    它的add(Term term)方法可以向其内部添加关键字,然后可以调用setSlop(int slop)设定一个“坡度“的变量确定关键词之间是否允许、允许多少个无关词汇的存在
     Query query=new PhraseQuery();
     query .add(Term(“field” , ”keyword”));

query .add(Term(“field1” , ”keyword1”));

query .setSlop(3);

     Hits hits=searcher.search(query);

对于两个紧密相连的关键词来说无论将坡度设置多少Lucene总能找到它所在的文档,如果两个不紧密相连的关键词如果坡度设置过小则无法找到。

(6)PhrasePrefixQuery短语缀搜索
    PhrasePrefixQuery和Phrase有些类似,如果用户向查找短语”david robert”,又想查找”mary robert”,可以构建2个PhraseQuery,然后使用BooleanQuery将他们作为子句,用“或”操作符连接。PhrasePrefixQuery可以让用户方便的实现这种方法。实例:

Hits hits=null;

         PhrasePrefixQuery query = new PhrasePrefixQuery();

         query.add(new Term[]{new Term(“field”,”keyword”) , new Term(“field”,”keyword1”)});

         query.add(new Term(“field”,”keyword2”));

         query.setSlop(2);

         hits=searcher.search(query);

(7)FuzzyQuery 相近词语的搜索

   FuzzyQuery同样继承自Query类,使一种模糊查询,可以简单的识别两个相近的词语。构造函数有三个:
         (1) public FuzzyQuery(Term term)
         (2) public FuzzyQuery(Term term, float minimumSimilarity) throws IllegalArgumentException
         (3)public FuzzyQuery(Term term, float minimumSimilarity, int prefixLength) throws IllegalArgumentException

minimumSimilarity 最小类似分值
prefixLength 前缀长度

实例
         IndexSearcher searcher=new IndexSearcher(“c://temp”);

         Term term=new Term(“field”,”keyword”);

         FuzzyQuery query=new FuzzyQuery(term);

         Hits hits=searcher.search(query);

(8)WildcardQuery 通配符搜索
         WildcardQuery继承自MultiTermQuery抽象类,可以进行通配符查询。实例:

         WildcardQuery query = null;

query = new WildcardQuery(new Term(“field”,”*keyword”));

Hits hits=searcher.search(query);

query=new WildcardQuery(new Term(“field”,”?keyword”));

hits=searcher.search(query);

通配符”?”表示一个字符,“*”表示0到多个字符。WildcardQuery和FuzzyQuery 由于要对字段关键字进行字符串匹配,所以在搜索的性能上面会受到一些影响

(9)QueryParser 查询分析器
     QueryParser位于org.apache.lucene.queryParser包,实现QueryParserConstants类,将用户输入的字符串转化为内部的Query或者Query组,构建各种各样的Query,也允许通过QueryParser来生成各种各样的Query子对象,这使Lucene查询功能变得更加灵活和强大

简单实例:

Query query=null;

Hits hits=null;

IndexSeacher searcher =new IndexSearcher(“c:\\temp”);

query =QueryParser.parse(“keyword”,”field”,new StandarAnalyzer());

        hits =searcher.search(query);

QueryParser实际上使一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象

static public Query parse(String query, String field, Analyzer analyzer)

         搜索表达式

“david”

在默认的字段中索检”david”关键字

“content:david”

在”content”字段中索检”david”关键字

“david mary”或”david OR mary”

在默认字段中索检关键字”divid”或”mary”,

“+david +mary”或”david AND mary”

在默认字段中索检关键字”divid”与”mary”

“content:david –title:manager”或”content:david AND NOT title: manager”

在字段content中索检关键字david,但在title字段中不包含manager关键字

“(david OR mary) AND robert”

在默认字段中索检david或mary关键字,并且包含有robert关键字

“davi*”

在默认字段中索检前缀为davi

“content : “david is a manager ””

在content字段中索检短语 david is a manager

在QueryParser对用户输入进行扫描时,需要给它一个分析器,必须要和建立索引时的分析器一样,这样才能保证分析成功

posted @ 2010-09-01 12:03 雪山飞狐12345 阅读(71) 评论(0) 编辑


1、多字段搜索就是同时要一个以上的字段中的内容进行比较搜索,类似概念在SQL中就是select * from Table where a like '%query%' or b like '%query%'。

Lucene.net中的单个字段查询大家都比较熟悉,这里对字段content进行搜索
Query query = QueryParser.Parse(querystr,"content",new ChineseAnalyzer());
Hits hits = searcher.Search(query);

对多个字段查询用到一个MultiFieldQueryParser对象,该对象继承自Query,我们要对字段title,content进行搜索。
string[] fields = {"content","title"};
Query multiquery = MultiFieldQueryParser.Parse(querystr,fields,new ChineseAnalyzer());
Hits hits = searcher.Search(multiquery);

2、多索引目录就是要在多个索引目录的中进行比较搜索,类似概念在SQL中就是select * from TableA union select * from TableB。
IndexSearcher[] searchers = new IndexSearcher[2];
searchers[0] = new IndexSearcher(IndexPath0);
searchers[1] = new IndexSearcher(IndexPath1);

MultiSearcher multisearcher = new MultiSearcher(searchers);
TopDocs multitopdocs = multisearcher.Search(query, null, 1000);
这个搜索的结果可能有相同的信息,比如你有一条相同的信息在多个目录中索引,搜索的结果就会出现多次相同的信息。

还有一种搜索方式是用到ParallelMultiSearcher这个对象,它是从MulitSearcher继承而来。
ParallelMultiSearcher parallelmultisearcher = new ParallelMultiSearcher(searchers);
TopDocs paralleltopdocs = parallelmultisearcher.Search(query, null, 1000);
这个搜索是对搜索后的结果进行合并,剔除重复的信息。

posted @ 2010-09-01 10:25 雪山飞狐12345 阅读(63) 评论(0) 编辑
《Internet狂采大师》是一款支持多任务、多线程的互联网信息采集系统,通过灵活的规则可以从任何类型的网站采集信息,如新闻网站、论坛、博客、电子商务网站、招聘网站等等;支持网站登录采集、POST采集、脚本页面采集、动态页面采集等高级采集功能;高级版支持树形多分支采集、XPATH可视化采集;拥有详细的日志系统、功能强大的调度系统等等。
 
软件功能特点:
1、 多任务 & 多线程,可以同时执行多个采集任务,每个任务又可以使用多个线程;
2、 功能设计简洁容易上手,所见即所得,所采即可见;
3、 采集功能强大,可以采集新闻网站、论坛、博客、电子商务网站、招聘网站等海量数据采集;
4、支持多种编码:UTF8、UNICODE、GBK等,软件会自动转换;
5、支持多种站点类型:包括HTML、AJAX等;
6、支持登陆、验证后采集;
7、支持POST网址捕获,可以轻松采集以POST方式提交的网站;
8、支持URL树形多分支导航,一个任务轻松采集论坛多个版块(目前同类软件具有此功能的极其少);
9、采集任务可以进行分类,采用树型目录管理
10、支持关键字采集,直接输入关键字,一步设置;
11、支持增量采集,适用于短时间内对论坛,博客进行增量更新;
12、支持断点采集,不用担心断电或软件错误而使得任务中断(此功能区别于其他同类软件的优势在于:真正实现断点采集,服务器重新启动后照样可以继续采集<注意:是接着服务器关机前的状态进行继续采集,而非暂停后的继续>);
13、秒级任务调度,管理任务更加智能,当有大量任务同时运行时,可以分批、排队;
14、批量任务管理,支持批量导入、导出/启动、停止;
15、XPATH可视化采集已遥遥领先于市面其他采集软件;
16、采集结果自动排重,同时可以对采集结果进行筛选和处理;
17、完全结构化抽取,数据保存到本地,多数据库支持,包括ACCESS、 SQL Server、 My Sql、Oracle;
18、软件运行稳定,采集速度快,占用资源少,支持数百个任务并发稳定地运行,适合企业级应用;
 
官方网站:http://www.kget.cn/
posted @ 2010-06-04 11:27 雪山飞狐12345 阅读(170) 评论(0) 编辑

终极旋风是一款.net 平台开发的支持多任务、多线程的集数据采集、清洗、抽取、格式化、入库、倒排索引的高性能垂直搜索引擎后台。

从V0.9开始,终级旋风彻底区别了目前市场上的采集软件。

小旋风三大应用:
1)与传统采集软件(火车头、网络神采等)相似,对列表页->内容页这样的数据进行精准批量采集。
2)建立“酷讯”、”Google生活“这样的垂直搜索引擎。支持全站按城市、频道spider路由,数据入库并倒排索引,并支持定时更新,
支持秒级更新配置,做“会冒泡”的垂直搜索引擎。

2、作者联系方式

QQ:86820609  MSN/email: jerry_weijb@163.com

官方QQ群: 20000042

请注明:来自cnblog或小旋风

欢迎如下合作:

1、技术交流 2、承接垂直搜索项目 3、按需提供或定制部分功能模块源代码【适当费用】 4、其他一切形式的合作...

终级旋风技术支持论坛: http://endso.cn/
软件下载地址:http://www.endso.cn/index.php?action-viewnews-itemid-70-php-1 

posted @ 2009-08-15 15:23 雪山飞狐12345 阅读(223) 评论(0) 编辑

1、小旋风垂直搜索平台是什么?

小旋风是一款.net 平台开发的支持多任务、多线程的集数据采集、清洗、抽取、格式化、入库、倒排索引的高性能垂直搜索引擎后台。

V0.9开始,小旋风彻底区别了目前市场上的采集软件。

小旋风三大应用:
1)与传统采集软件(火车头、网络神采等)相似,对列表页->内容页这样的数据进行精准批量采集
2)建立“酷讯”、”Google生活“这样的垂直搜索引擎。支持全站按城市、频道spider路由,数据入库并倒排索引,并支持定时更新,
支持秒级更新配置,做“会冒泡”的垂直搜索引擎

2、作者联系方式

QQ:86820609  MSN/email: jerry_weijb@163.com

官方QQ群20000042

请注明:来自cnblog或小旋风

欢迎如下合作:

1、技术交流 2、承接垂直搜索项目 3、按需提供或定制部分功能模块源代码【适当费用】 4、其他一切形式的合作...

小旋风技术支持论坛: http://51miner.cn/

4、下载信息  帮助信息new

官方下载地址

下载地址: 小旋风安装程序V1.0.8.rar 4.18更新】(3.32M,RAR压缩)

更新时间:2009-04-19  17:02

1、核心逻辑重写,保证软件的稳定、高效、扩展性。

2、支持采集后内容的处理,包括内容过滤、替换等等。

3、支持登录后采集信息。

4、修正数据库连接的一些问题。

5、优化匹配方式(前、后缀方式)可视化规则向导。

6、一些bug及体验问题。

 
更新时间:2009-03-22  21:26

1、支持post数据取数据,支持51job,携程这样的网站
2、支持脚本式抽取url,javascript:OLJD(N)这样的url可以轻松取到。
3、优化xpath可视化抽取向导。
4、修改部分bug
5、优化性能

版本号:1.0.2

更新时间:2009-03-17  22:46

更新说明:
1、支持网页编码自动识别。
2、启用爬虫路径路由后,网页层次自动计算。
3、修正密码框不显示*的问题。
4、前、后缀抽取的大小写的bug.
5、其它一些bug及体验问题。

版本号:1.0.1_090315

更新说明:

1、优化Xpath可视化模板生成向导,使之能够适应qq.com,163.com这样复杂的门户网站。
2、数据库连接配置向导优化。
3、多数据库支持完成,包括Access/Sql Server/My SQL
4、支持lucene.net 2.3支持索引
5、支持中文分词
6、修改部分bug.

 

版本号:1.0_090303
更新时间:2009-03-03  01:25

 

更新说明:

1、体验、性能优化

2、新增任务示例及帮助文件

3、修改部分bug及压力测试

版本号:1.0
更新时间:2009-02-28  14:25

更新说明:

1、解决数据过w之后软件假死的问题

2、优化批量数据入库的性能

3、新增线程延迟,缓解目标网站的压力

4、解决数据入库重复的问题

5、修改部分bug

版本号:0.9
更新时间:2009-02-21  0:21

更新说明:
1、抽取向导定位不精确的问题

2、新增spider路由功能

3、新闻、论坛、blog等网页正文无模板抽取

4、软件测试、性能优化,修复部分bug

5、用vs自带的打包程序取代NSIS

版本号:0.8
更新时间:2009-02-15  14:54

更新说明:
1、修正模板向导部分网页乱码的问题

--------------------------------------------------------
版本号:0.7

更新时间:2009-02-14  20:37

更新说明:
1、重写数据模板规则

2、支持sql server

3、支持任务调度

4、体验优化及bug修改
----------------------------------------------------

版本号:0.6

更新时间:2009-02-08  15:00

更新说明:
1、修复数据连接的异常

--------------------------------------------------------------
版本号:0.5

更新时间:2009-02-07  22:00
更新说明:
1、优化软件易用性,去除非核心的设置项
2、修正了部分Bug
3、重构任务设置中心

4、可视化的xpath模板生成向导(类似于firebug + xpather的效果)

posted @ 2009-04-20 23:50 雪山飞狐12345 阅读(123) 评论(0) 编辑
posted @ 2009-02-21 00:23 雪山飞狐12345 阅读(2005) 评论(16) 编辑
posted @ 2009-02-14 22:15 雪山飞狐12345 阅读(439) 评论(1) 编辑
仅列出标题  下一页