美文网首页爬虫程序员dotNET
4.1、苏宁百万级商品爬取 代码讲解 索引建立

4.1、苏宁百万级商品爬取 代码讲解 索引建立

作者: HapplyFox | 来源:发表于2018-07-11 11:20 被阅读0次

    接下去我们进行索引建立,本项目索引建立我们使用Lucene.Net。在使用前我们介绍以下Lucene是什么!

    Lucene概述

    Lucene是一款高性能的、可扩展的信息检索(IR)工具库。信息检索是指文档搜索、文档内信息搜索或者文档相关的元数据搜索等操作。

    索引过程:

    ①获取内容

    ②建立文档
    获取原始内容后,就需要对这些内容进行索引,必须首先将这些内容转换成部件(通常称为文档),以供搜索引擎使用。文档主要包括几个带值的域,比如标题、正文、摘要、作者和链接。

    ③文档分析
    搜索引擎不能直接对文本进行索引:确切地说,必须将文本分割成一系列被称为语汇单元的独立的原子元素。每一个语汇单元大致与语言中的“单词”对应起来。

    ④文档索引
    在索引步骤中,文档被加入到索引列表。

    Lucene 的参考链接,想多了解的小伙伴可以点击
    借助 Lucene.Net 构建站内搜索引擎
    使用Lucene.Net实现全文检索
    Lucene.Net+盘古分词器(详细介绍)

    在阅读上述内容和文章链接后,相信大家对Lucene是什么有了一定的了解。那么我们再来说说分词,分词我们简单理解是这样的
    “今天是个好日子”通过分词中间件,我们能够得到一个集合,集合内容为["今天","是","一个","好日子"]这样的内容,相当于把内容分解成了我们日常理解的词汇。中文分词现在有很多种
    庖丁解牛,盘古分词,结巴分词,IK分词等等,大家可以通过百度对分词组件进行了解,这里也不做多的说明。

    本项目选用的分词组件是 盘古分词,采用Lucene.Net建立索引
    索引建立是基于当前已经存在的20张表


    image.png

    我们是对这些数据建立索引,那我们如何高效的建立索引,当然也是多线程啦!😄

    思路说明

    遍历20张表
    第一步、得到每张表的个数集合 Dictionary[表名,分页总数]
    我们的分页以1000作为基数,即一次取1000条数据
    _commodityService.GetTableCount(i) 得到当前表格的个数 (i是商品表索引号)
    PageHelper.GetPageNum(A,B) A是集合,B是个数,得到的集合是对当前表的数据每次取1000个,需要分多少页

       public static int GetPageNum(int allCount, int pageSize)
            {
                int PageNum = 0;//任务分页个数
                if (allCount % pageSize == 0)
                {
                    PageNum = allCount / pageSize;
                }
                else
                {
                    PageNum = allCount / pageSize + 1;
                }
    
                return PageNum;
            }
    
                 //分页以1000作为基数
                //Dictionary[表名,分页总数]
                Dictionary<int, int> tableCountDictionary = new Dictionary<int, int>();
                //StaticConst.CategorySheetCount=20
                for (int i = 0; i < StaticConst.CategorySheetCount; i++)
                {
                    int pageNum = PageHelper.GetPageNum(_commodityService.GetTableCount(i), StaticConst.PageGetCount); ;
                    tableCountDictionary.Add(i, pageNum);
                }
    

    第二步、得到[表索引,页码]集合

    对第一步的Dic字典循环,我们又得到一个新的集合列表,列表的内容是【表索引,分页索引】的集合
    集合的例子是:[{0,1},{0,2},{0,3}]
    解释,第一张表第一页,第一张表第二页,第一张表第三页这样的集合

       public class TableIndexModel
        {   /// <summary>
            /// 表索引
            /// </summary>
            public int TableIndex { get; set; }
            /// <summary>
            /// 分页索引
            /// </summary>
            public int PageIndex { get; set; }
        }
    
                //得到[表索引,页码]集合
                List<TableIndexModel> timList = new List<TableIndexModel>();
                foreach (var tcd in tableCountDictionary)
                {
                    for (int i = 1; i <= tcd.Value; i++)
                    {
                        timList.Add(new TableIndexModel()
                        {
                            TableIndex = tcd.Key,
                            PageIndex = i
                        });
                    }
                }
    

    第三步、定义每一个线程需要完成的内容
    根据第二步骤,我们得到了一个[表索引,页码]的集合,接下去我们开始分配每个线程要完成的任务量
    如果我们集合个数是3000,我们对其3000进行分页,最好是将页数定义的多一点,这样每个集合处理的任务量少,耗时少。

    执行完上述代码后我们可以得到一个List<[表索引,页码]>的集合,这个集合就是我们最终得到的集合。这个集合的好处如果大家看得懂代码,能够体会到好处,那就是任务分配均匀,每个集合要处理的任务数都是相同的,这样多线程处理的时候就不会有快慢之分。能够快速切换任务和节约执行时间。

                 /*平均分配任务的业务逻辑为:
                    每个线程需要处理多少任务=timList.Count 总个数 /分页数
                 */
                int workPageNum = PageHelper.GetPageNum(timList.Count, threadCount);
    
                //得到[平均分配后的任务列表]
                List<List<TableIndexModel>> taskDataList = new List<List<TableIndexModel>>();
                for (int i = 1; i <= workPageNum; i++)
                {
                    var list = timList.Skip((i - 1) * threadCount).Take(threadCount).ToList();
                    taskDataList.Add(list);
                }
    

    第四步、多线程处理
    如下代码因为是部分贴图,所以可能理解起来较为困难,如果有问题的观众可以直接下载github上面的源码,自己调试看看效果,有问题也可以email给我。
    代码的大概意思是,开启20个线程,20个线程处理第三步得到的集合。

    • 得到一个随机编码,这是索引存储的Lucene文件夹名称,判断编码是否存在,如果不存在加入编码list集合
    • 对当前集合建立索引
    • 将当前任务加入List<Task>集合,判断任务集合是否超出20上限,如果超出,等待集合中任务完成。这一步是用来手动限定20个线程数量的。
    • 在所有任务结束后,对当前的编码集合进行索引合并。
      在索引建立时,如果存在错误,即认定索引建立失败,结束所有的任务
                   List<Task> taskList = new List<Task>();
                    var threadNums = Enumerable.Range(1, StaticConst.CategorySheetCount).ToList();
                    Random random = new Random();
                    int index = 0, alltakCount = taskDataList.Count;
                    foreach (var taskData in taskDataList)
                    {
                        index++;
                        var threadCode = CommodityDAL.GetTName(random.Next(1, threadNums.Count));
                        Task task = taskFactory.StartNew(() =>
                        {
                            try
                            {
                                LuceneBulid luceneBuild = new LuceneBulid();
                                if (!PathSuffixList.Any(u => u == threadCode))
                                {
                                    PathSuffixList.Add(threadCode);
                                }
                                //建立索引
                                luceneBuild.BuildIndex(taskData, threadCode, true);
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine($"BuildIndexError\t{ex.Message}");
                                CTS.Cancel();
                            }
                        }, CTS.Token);
                        taskList.Add(task);
                        if (taskList.Count > 20)
                        {
                            taskList = taskList.Where(t => !t.IsCompleted && !t.IsCanceled && !t.IsFaulted).ToList();
                            Task.WaitAny(taskList.ToArray());
                        }
                    }
                    taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), MergeAllLuceneIndex));
    
          private void MergeAllLuceneIndex(Task[] obj)
            {
                try
                {
                    ILuceneBulid builder = new LuceneBulid();
                    builder.MergeAllLuceneIndex(PathSuffixList.ToArray());
                    OnTaskComplate(new EventArgs());//任务完成触发事件
                }
                catch (Exception ex)
                {
                    StringValueEventArgs e = new StringValueEventArgs() { Value = ex.Message };
                    OnTaskError(e);//每完成一个任务触发事件
                    Console.WriteLine($"MergeAllLuceneIndex\t{ex.Message}{ex}");
                }
            }
    

    相关文章

      网友评论

        本文标题:4.1、苏宁百万级商品爬取 代码讲解 索引建立

        本文链接:https://www.haomeiwen.com/subject/mrncpftx.html