Hadoop学习之自己动手做搜索引擎【网络爬虫+倒排索引+中文分词】

一、使用技术

  • Http协议
  • 正则表达式
  • 队列模式
  • Lucenne中文分词
  • MapReduce

二、网络爬虫

  1. 项目目的
    通过制定url爬取界面源码,通过正则表达式匹配出其中所需的资源(这里是爬取csdn博客url及博客名),将爬到的资源存入文件中便于制作成倒排索引。根据页面源码垂直爬取csdn网站中的所有博客资源(找到一个超链接就爬取该超链接中的内容)。
  2. 设计思想
    建立一个队列对象,首先将传入的url存入代表未爬取的队列中,循环如果未爬取队列中所有url进行爬取,并将爬取的url转移到代表已爬取的队列中。使用HttpURLConnection获得页面信息,使用正则表达式从页面信息中所需的信息输出到文件中,并将从页面信息中匹配到的超链接存入代表未爬取的队列中,实现垂直爬取数据。
  3. 源码及分析
    a.LinkCollection.java
package com.yc.spider;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 链接地址队列
 * @author wrm
 *当爬到一个超链接后,将其加入到队列中,接着爬这个超链接,并将这个超链接放入标示已查的队列中
 */
public class LinkCollection {
    //待访问url的集合:队列
    private List<String> unVisitedUrls=Collections.synchronizedList(new ArrayList<String>());
    private Set<String> visitedUrls=Collections.synchronizedSet(new HashSet<String>());

    /**
     * 入队操作
     */
    public void addUnVisitedUrl(String url){
        if(url!=null&&!"".equals(url.trim())&&!visitedUrls.contains(url)&&!unVisitedUrls.contains(url)){
            unVisitedUrls.add(url);
        }
    }
    /**
     * 出队
     */
    public String deQueueUnVisitedUrl(){
        if(unVisitedUrls.size()>0){
            String url=unVisitedUrls.remove(0);
            visitedUrls.add(url);
            return url;
        }
        return null;
    }
    /**
     * 判断队列是否为空
     */
    public boolean isUnVisitedUrisEmpty(){
        if(unVisitedUrls!=null&&!"".equals(unVisitedUrls)){
            return false;
        }else{
            return true;
        }
    }
    /**
     * hadoop出队
     */
    public String deQueueVisitedUrl(){
        if(visitedUrls.iterator().hasNext()){
            String url=visitedUrls.iterator().next();
            visitedUrls.remove(0);
            return url;
        }
        return null;
    }
    /**
     * 判断Visited队列是否为空
     */
    public boolean isVisitedUrisEmpty(){
        if(visitedUrls!=null&&!"".equals(visitedUrls)){
            return false;
        }else{
            return true;
        }
    }
}

该类是url的队列,该说的注释中都有

b.DownLoadTool.java

package com.yc.spider;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;

/**
 * 下载工具类
 * @author wrm
 *
 */
public class DownLoadTool {
    /**
     * 编码集
     */
    private String encoding="GBK";
    /**
     * 下载的文件保存的位置
     */
    private String savePath=System.getProperty("user.dir")+File.separator;

    /**
     * 自动生成保存的目录
     * 目录名的命名规范:yyyyMMddHHmmss
     */
    public static File createSaveDirectory(){
        DateFormat df=new SimpleDateFormat("yyyyMMddHHmmss");
        String directoryName=df.format(new Date());
        return createSaveDirectory(directoryName);
    }

    /**
     * 根据指定目录名
     * @param directoryName
     * @return
     */
    public static File createSaveDirectory(String directoryName) {
        File file=new File(directoryName);
        if(!file.exists()){
            file.mkdirs();
        }
        return file;
    }
        /**
         * 下载页面的内容
         */
        static String downLoadUrl(String addr){
            StringBuffer sb=new StringBuffer();
            try {
                URL url=new URL(addr);
                HttpURLConnection con=(HttpURLConnection) url.openConnection();

                con.setConnectTimeout(5000);
                con.connect();
                //产生文件名

                Random r=new Random();
                try {
                    Thread.sleep(r.nextInt(2000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(con.getResponseCode());
                System.out.println(con.getHeaderFields());
                if(con.getResponseCode()==200){
                    BufferedInputStream bis=new BufferedInputStream(con.getInputStream());
                    Scanner sc=new Scanner(bis,encoding);
                        while(sc.hasNextLine()){    //读取拼接页面信息
                        sb.append(sc.nextLine());
                    }
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return sb.toString();
        }


}

该类使用HttpURLConnection.getInputStream()获得页面内容,其中

                Random r=new Random();
                try {
                    Thread.sleep(r.nextInt(2000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

是为了防止被网站识别出是爬虫在访问而进行的睡眠操作
con.getResponseCode()==200是判断访问该网页获得的状态码是否为200(成功)
如果想要获得http头的话可以使用以下代码

con.getHeaderField(name);   //获得头中的name数据
con.getHeaderFields();      //获得头中的所有数据

某些网站的防爬虫做得实在太好!就算睡眠了也依旧不让你爬,这时可以冲firfox中获得头,通过该请求头方面便可骗过。

c.HtmlNodeParser.java

package com.yc.spider;

import java.util.HashSet;
import java.util.Set;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

public class HtmlNodeParser {
    /**
     * 解析url地址中对应的页面中的a标签与frame标签
     * @throws ParserException 
     * 
     */
    public Set<String> parseNode(String url,NodeFilter filter) throws ParserException{      //NodeFilter表明是否要全网爬行
        Set<String> set=new HashSet<String>();
        Parser parser=new Parser(url);
        if(!url.startsWith("http:/")){
            url="http:/"+url;
        }
        //这个过滤器用户过滤frame
        NodeFilter framefilter=new NodeFilter(){

            @Override
            public boolean accept(Node node) {
                if(node.getText().indexOf("frame src=")>=0){
                    return true;
                }else{
                    return false;
                }
            }

        };
        //创建过滤器     LinkTag表示超链接标记
        OrFilter linkFilter=new OrFilter(new NodeClassFilter(LinkTag.class),framefilter);

        NodeList list=parser.extractAllNodesThatMatch(linkFilter);

        for(int i=0;i<list.size();i++){
            Node node=list.elementAt(i);
            String linkurl=null;
            if(node instanceof LinkTag){    //href
                LinkTag linkTag=(LinkTag) node;
                linkurl=linkTag.getLink();


            }else{
                //是frame节点 src
                String frame=node.getText();
                int start=frame.indexOf("src=");
                frame=frame.substring(start);
                int end=frame.indexOf(" ");
                if(end==-1){
                    end=frame.indexOf(">");
                }
                linkurl=frame.substring(4,end-1);
            }
            if(linkurl==null||"".equals(linkurl)||(!linkurl.startsWith("http://")&&!linkurl.startsWith("https://"))){
                continue;
            }
            if(  filter!=null&&filter.accept(node)==false){
                continue;
            }


            set.add(linkurl);
        }
        return set;
    }
}

d.TitleDown.java

package com.yc.spider;

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TitleDown {
    /**
     * 取html标记
     */
    static String A_URL="<\\s*a\\s+([^>]*)\\s*>([^<]*)</a>";
    static String HREF_URL="href\\s*=\\s*\"*(http://blog.csdn.net/?.*?/article/details/?.*?)(\"|>|\\s+)";
//  static String HREF_URL="href\\s*=\\s*\"*(topic/?.*?)(\"|>|\\s+)";
//  static String HREF_URL="href\\s*=\\s*\"*(http://news.sohu.com/?.*?)(\"|>|\\s+)";


    static Set<String> getImageLink(String html){
        System.out.println(html);
        Set<String> result=new HashSet<String>();
        String g1="";
        //创建一个Pattern模式类,编译这个正则表达式
        Pattern p=Pattern.compile(A_URL,Pattern.CASE_INSENSITIVE);
        Pattern p1=Pattern.compile(HREF_URL, Pattern.CASE_INSENSITIVE);
        //定义一共饿 匹配器的类
        Matcher matcher=p.matcher(html);
        while(matcher.find()){
            g1=matcher.group(1);
            Matcher m1=p1.matcher(g1);
            while(m1.find()){
                String word=matcher.group(2);
                result.add(m1.group(1)+"\t"+word.trim().trim());
            }
        }

        return result;
    }


    public static void main(String[] args) {
        String addr="http://www.csdn.com";
        String html=DownLoadTool.downLoadUrl(addr);


//      String html="<title>根本没问题啊!</title>";
        System.out.println(html);
        Set<String> imagetags1=getImageLink(html);

        for(String imagetag:imagetags1){

            System.out.println(imagetag);
        }

    }
}

该类使用正则表达式来匹配我所需要的数据。

static String A_URL="<\\s*a\\s+([^>]*)\\s*>([^<]*)</a>";

用于匹配a标签和a标签中的内容

static String HREF_URL="href\\s*=\\s*\"*(http://blog.csdn.net/?.*?/article/details/?.*?)(\"|>|\\s+)";

用于匹配url,因为这里我是要csdn的博客地址,所以作此匹配

e.Spider.java

package com.yc.spider;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.htmlparser.util.ParserException;


public class Spider {

    private LinkCollection lc=new LinkCollection();
    private DownLoadTool dlt=new DownLoadTool();
    private HtmlNodeParser hnp=new HtmlNodeParser();

    public String getFileName(String url){
        String filename=url.toString().substring(7);
        filename=filename.replaceAll("/", "-");
        filename=filename.replace(".", ",");
        return filename;
    }

    public void crawling(String url,String directory) throws FileNotFoundException{
        //1.先添加url到待取队列中
        lc.addUnVisitedUrl(url);
        try {
            Configuration conf=new Configuration();
            URI uri=new URI("hdfs://192.168.1.123:9000");   //hdfs主机uri
            FileSystem hdfs=FileSystem.get(uri, conf);
            //2.循环这个队列,到这个队列为空时
            while(lc.isUnVisitedUrisEmpty()==false){
                //3.取出待取地址
                String visiturl=lc.deQueueUnVisitedUrl();
                //4.下载这个页面
                try {
                    String html=dlt.downLoadUrl(visiturl);
                    Set<String> allneed=TitleDown.getImageLink(html);
                    for (String addr : allneed) {
                        String a=addr.substring(addr.indexOf("\t")+1);
                        String filename=addr.substring(0,addr.indexOf("\t"));
                        filename=getFileName(filename);
                        System.out.println(filename);
                        Path p=new Path("/spider/"+filename);
                        FSDataOutputStream dos=hdfs.create(p);
                        try {
                            System.out.print(a);
                            dos.write(a.getBytes());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }finally {
                            dos.close();    //这里一定要将dos关闭,不然内容无法写入
                        }
                    }
                    //5.从页面中分析出超链接地址,放入待取地址中
                    Set<String> newurl=hnp.parseNode(visiturl, null);
//                  dlt.createLogFile(TitleDown.getImageLink(html));
                    //将这些地址又加入到待取地址中
                    for(String s:newurl){

                        String httpregex="http://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?";
                        Pattern p2=Pattern.compile(httpregex,Pattern.CASE_INSENSITIVE);
                        Matcher matcher=p2.matcher(s);
                        while(matcher.find()){
                            lc.addUnVisitedUrl(s);
                            //boolean b=matcher.
                        }

                    }
                } catch (ParserException e) {
                    e.printStackTrace();
                }
            }

        } catch (IllegalArgumentException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (URISyntaxException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

    }

}

因为我要将URL作为文件名,而文件名不能含有某些字符,所以用该方法进行替换

public String getFileName(String url){
        String filename=url.toString().substring(7);
        filename=filename.replaceAll("/", "-");
        filename=filename.replace(".", ",");
        return filename;
    }

生成的文件
这里写图片描述
每一个文件中只有改a标签的内容(其实还可以加入该页面的头,但是这里没做这么复杂)

三、倒排索引制作

  1. 设计目的
    使用MapReduce及中文分词将爬到的文件制作成倒排索引,索引文件格式为
    Key(分词器分出的词)+“\t”+url1:sum;url2:sum

  2. 设计思想及源码
    在Map阶段获得文件名,并将文件名还原为url,作为value。将文件内容通过分词器分词后将分出的每个词作为key,输出。
    源码:

public static class InvertedIndexMapper extends Mapper<Object, Text, Text, Text>{

        private Text keyInfo = new Text();  // 存储单词和URI的组合
        private Text valueInfo = new Text(); //存储词频
        private FileSplit split;  // 存储split对象。

        @Override
        protected void map(Object key, Text value, Mapper<Object, Text, Text, Text>.Context context)
                throws IOException, InterruptedException {

            //获得<key,value>对所属的FileSplit对象。
            split = (FileSplit) context.getInputSplit();

            Analyzer sca = new SmartChineseAnalyzer( );  

            TokenStream ts = sca.tokenStream("field", value.toString());  
            CharTermAttribute ch = ts.addAttribute(CharTermAttribute.class);  

            ts.reset();  
            while (ts.incrementToken()) {  
                System.out.println(ch.toString());  
                String url=split.getPath().toString();
                url=url.substring(url.lastIndexOf("/"));
                url=url.replaceAll("-", "/");
                url=url.replace(",", ".");
                url="http:/"+url;
                System.out.println(url);
                // key值由单词和URI组成。
                keyInfo.set( ch.toString()+";"+url);
                //词频初始为1
                valueInfo.set("1");
                context.write(keyInfo, valueInfo);
            }  
            ts.end();  
            ts.close();  
        }
    }

Combiner阶段:将相同key值的词频累加获得词频

public static class InvertedIndexCombiner extends Reducer<Text, Text, Text, Text>{

        private Text info = new Text();

        @Override
        protected void reduce(Text key, Iterable<Text> values, Reducer<Text, Text, Text, Text>.Context context)
                throws IOException, InterruptedException {

            //统计词频
            int sum = 0;
            for (Text value : values) {
                sum += Integer.parseInt(value.toString() );
            }

            int splitIndex = key.toString().indexOf(";");

            //重新设置value值由URI和词频组成
            info.set( key.toString().substring( splitIndex + 1) +":"+sum );

            //重新设置key值为单词
            key.set( key.toString().substring(0,splitIndex));

            context.write(key, info);
        }
    }

Reducer阶段,组合出最后的数据输出

public static class InvertedIndexReducer extends Reducer<Text, Text, Text, Text>{

        private Text result = new Text();

        @Override
        protected void reduce(Text key, Iterable<Text> values, Reducer<Text, Text, Text, Text>.Context context)
                throws IOException, InterruptedException {

            //生成文档列表
            String fileList = new String();
            for (Text value : values) {
                fileList += value.toString()+";";
            }
            result.set(fileList);

            context.write(key, result);
        }

    }

四、用户搜索模拟

原理:将用户数据的关键字分词后与倒排索引分别匹配,只要匹配到的在Combiner中统计词频,并在Reduce中操作后输出。
源码:

package com.yc.hadoop;

import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;



public class FindWord {

    public static class FindMapper extends Mapper<Text, Text, Text, Text>{



        @Override
        protected void map(Text key, Text value, Mapper<Text, Text, Text, Text>.Context context)
                throws IOException, InterruptedException {
            String text="android可行性";       //用户输入的关键字
            Analyzer sca = new SmartChineseAnalyzer( );  

            TokenStream ts = sca.tokenStream("field", text);  
            CharTermAttribute ch = ts.addAttribute(CharTermAttribute.class);  

            ts.reset();  
            while (ts.incrementToken()) {  
                if(ch.toString().equals(key.toString())||ch.toString().equals(key.toString())){
                    System.out.println(value.toString());
                    String[] urls=value.toString().split(";");
                    int count=0;
                    for (String url : urls) {
                        String oneurl=url.split(":")[0]+url.split(":")[1];
                        count=Integer.parseInt(url.split(":")[2]);
                        String newvalue=ch.toString()+";"+count;
                        System.out.println(">>>>>>>>"+oneurl+">>>>>>>>>>"+newvalue);
                        context.write(new Text(oneurl),new Text( newvalue));
                    }

                }
            }  
            ts.end();  
            ts.close();  



        }
    }

    public static class FindCombiner extends Reducer<Text, Text, Text, Text>{
        @Override
        protected void reduce(Text key, Iterable<Text> values, Reducer<Text, Text, Text, Text>.Context context)
                throws IOException, InterruptedException {

            //统计词频
            int sum = 0;
            for (Text value : values) {
                String count=value.toString().split(";")[1];
                sum += Integer.parseInt(count );
            }
            context.write(new Text(String.valueOf(sum)),new Text(key.toString()) );
        }
    }


    public static class FindReducer extends Reducer<Text, Text, Text, Text>{

        @Override
        protected void reduce(Text key, Iterable<Text> values, Reducer<Text, Text, Text, Text>.Context context)
                throws IOException, InterruptedException {

            //生成文档列表
            for (Text text : values) {
                context.write(key, text);
            }


        }

    }
    public static void main(String[] args) {


            try {
                Configuration conf = new Configuration();

                Job job = Job.getInstance(conf,"InvertedIndex");
                job.setJarByClass(InvertedIndex.class);

                //实现map函数,根据输入的<key,value>对生成中间结果。
                job.setMapperClass(FindMapper.class);

                job.setMapOutputKeyClass(Text.class);
                job.setMapOutputValueClass(Text.class);
                job.setInputFormatClass(KeyValueTextInputFormat.class);
                job.setCombinerClass(FindCombiner.class);
                job.setReducerClass(FindReducer.class);

                job.setOutputKeyClass(Text.class);
                job.setOutputValueClass(Text.class);


                FileInputFormat.addInputPath(job, new Path("hdfs://192.168.1.123:9000/spiderout/1462887403514/part-r-00000"));
                FileOutputFormat.setOutputPath(job, new Path("hdfs://192.168.1.123:9000/1"));

                System.exit(job.waitForCompletion(true) ? 0 : 1);
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
}

结果展示:
测试

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
自己动手搜索引擎 1 第1章 了解搜索引擎 1 1.1 Google神话 1 1.2 体验搜索引擎 1 1.3 你也可以搜索引擎 4 1.4 本章小结 4 第2章 遍历搜索引擎技术 5 2.1 30分钟实现的搜索引擎 5 2.1.1 准备工作环境(10分钟) 5 2.1.2 编写代码(15分钟) 6 2.1.3 发布运行(5分钟) 9 2.2 搜索引擎基本技术 14 2.2.1 网络蜘蛛 14 2.2.2 全文索引结构 14 2.2.3 Lucene 全文检索引擎 15 2.2.4 Nutch网络搜索软件 15 2.2.5 用户界面 17 2.3 商业搜索引擎技术介绍 17 2.3.1 通用搜索 17 2.3.2 垂直搜索 18 2.3.3 站内搜索 19 2.3.4 桌面搜索 21 2.4 本章小结 21 第3章 获得海量数据 22 3.1 自己的网络蜘蛛 22 3.1.1 BerkeleyDB介绍 27 3.1.2 抓取网页 28 3.1.3 MP3 抓取 29 3.1.4 RSS 抓取 30 3.1.5 图片抓取 33 3.1.6 垂直行业抓取 34 3.2 抓取数据库中的内容 36 3.2.1 建立数据视图 36 3.2.2 JDBC数据库连接 36 3.2.3 增量抓取 40 3.3 抓取本地硬盘上的文件 41 3.3.1 目录遍历 41 3.4 本章小结 42 第4章 提取文档中的文本内容 43 4.1 从HTML文件中提取文本 43 4.1.1 HtmlParser介绍 51 4.1.2 结构化信息提取 54 4.1.3 网页去噪 60 4.1.4 网页结构相似度计算 63 4.1.5 正文提取的工具FireBug 64 4.1.6 正文提取的工具NekoHTML 66 4.1.7 正文提取 68 4.2 从非HTML文件中提取文本 73 4.2.1 TEXT文件 73 4.2.2 PDF文件 73 4.2.3 Word文件 82 4.2.4 Rtf文件 82 4.2.5 Excel文件 83 4.2.6 PowerPoint文件 84 4.3 流媒体内容提取 85 4.3.1 音频流内容提取 85 4.3.2 视频流内容提取 87 4.4 抓取限制应对方法 89 4.5 本章小结 90 第5章 自然语言处理 91 5.1 中文分词处理 91 5.1.1 Lucene 中的中文分词 91 5.1.2 Lietu中文分词的使用 92 5.1.3 中文分词的原理 92 5.1.4 查找词典算法 95 5.1.5 最大概率分词方法 98 5.1.6 新词发现 101 5.1.7 隐马尔可夫模型 102 5.2 语法解析树 104 5.3 文档重 105 5.4 中文关键词提取 106 5.4.1 关键词提取的基本方法 106 5.4.2 关键词提取的设计 107 5.4.3 从网页提取关键词 107 5.5 相关搜索 107 5.6 拼写检查 110 5.6.1 英文拼写检查 110 5.6.2 中文拼写检查 112 5.7 自动摘要 116 5.7.1 自动摘要技术 117 5.7.2 自动摘要的设计 117 5.7.3 Lucene中的动态摘要 124 5.8 自动分类 125 5.8.1 Classifier4J 126 5.8.2 自动分类的接口定义 127 5.8.3 自动分类的SVM方法实现 128 5.8.4 多级分类 128 5.9 自动聚类 131 5.9.1 聚类的定义 131 5.9.2 K均值聚类方法 131 5.9.3 K均值实现 133 5.10 拼音转换 138 5.11 语义搜索 139 5.12 跨语言搜索 143 5.13 本章小结 144 第6章 创建索引库 145 6.1 设计索引库结构 146 6.1.1 理解 Lucene 的索引库结构 146 6.1.2 设计一个简单的索引库 148 6.2 创建和维护索引库 149 6.2.1 创建索引库 149 6.2.2 向索引库中添加索引文档 149 6.2.3 删除索引库中的索引文档 151 6.2.4 更新索引库中的索引文档 151 6.2.5 索引的合并 151 6.2.6 索引的定时更新 152 6.2.7 索引的备份和恢复 153 6.2.8 修复索引 154 6.3 读写并发控制 154 6.4 优化使用 Lucene 155 6.4.1 索引优化 155 6.4.2 查询优化 157 6.4.3 实现时间加权序 162 6.4.4 实现字词混合索引 163 6.4.5 定制Similarity 170 6.4.6 定制Tokenizer 171 6.5 查询大容量索引 173 6.6 本章小结 174 第7章

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值