首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
V2EX  ›  分享创造

业余开发了一个技术搜索引擎,请小伙伴们提提意见。

  •  7
     
  •   nyfok · 94 天前 · 8562 次点击
    这是一个创建于 94 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,最近业余开发了一个搜索引擎,面向程序员领域,收录了国内技术网站和国外 github,stackoverflow 等网站。解决国内不能访问 google 的痛点,以及某些搜索广告太多的问题。

    纯粹手痒,做着玩玩看,目前蜘蛛的索引面还在不断增加中。在开发过程中优化了分词和 ranking 的算法,如果谁有好的 page ranking 算法建议,欢迎跟帖。

    欢迎大伙多提改进意见(拍砖请绕行),也请帮忙判断一下,这个产品有没有留着的必要,如果没有保留必要,我玩一阵子就准备把网站关了。

    谢谢大伙。ps:goobe.cn

    第 1 条附言  ·  83 天前
    应部分朋友要求,我简单写点开发步骤。今天先写抓取部分的开发实现:

    1、实现一套分布式计算系统。(原理参照 Map-Reduce,和 hadoop 有点像)
    业务逻辑简单解释如下:
    1 ) Gird 服务器从数据库载入初始任务,即一堆 URL 清单。
    2 ) Grid 服务器分析 URL,基于事先设定的模板 URL 兴趣规则,自动确定网页的分析模板 ID。
    3 ) Grid 服务器将“任务 URL ”和“模板 ID ”添加到客户端待处理队列。
    4 ) Grid 客户端通过 TCP 连接到 Grid 服务器,并维持心跳和状态检测。
    5 ) Grid 客户端从服务器获取任务,即 URL 和模板 ID,如果发现本地没有对应的模板分析代码,则根据模板 ID,实时从服务器请求一份模板代码。
    6 ) Grid 客户端从任务 URL 下载 HTML,并运行模板分析代码。模板分析代码会提取网页中有用的各个字段信息,并生成两类输出。第一类输出是 Pages,记录了一堆的子页面的 URL 和挖掘深度序号。第二类输出是 Document,是当前 URL 的结构化文档输出,譬如新闻页面,会输出标题、作者、来源、正文、缩略图、标签等字段。基于不同的模板输出 Document 字段不完全相同。
    7 ) Grid 客户端将 Pages 和 Document 提交到服务器。服务器将 Pages 去重,并确定更新逻辑后,将需要抓取的 pages 放到 Task Pool 里面等待执行。将 Document 信息添加或者更新到数据库的抓取完成 Document 库中。
    8 ) Grid 客户端重新获取新的任务进行执行。

    以上是简单解释,实际上分布式运算系统里面还包括客户端分组、执行任务类型管理等等功能。

    2、实现网页模板的快速开发功能。
    写一些 html 分析的基础类,譬如提取正文、枚举 href 链接、枚举 img、提取标题、JS decoder 等等东西。可以自己写,也可以用第三方框架,譬如 htmlagility 之类的。
    这部分工作主要要提高开发模板的效率,提高模板的兼容性,譬如抓下来的 html 不是严格的 xml 规范,用第三方的框架可能会解析失败,又比如第三方网站简单改版,模板是否能够自动适应。
    这部分工作,我目前做了可视化的模板代码生成工具,原理就是自己做了个浏览器,用鼠标在浏览器上选择节点,自动生成页面结构化内容输出代码。
    一直想做网站模板代码的自动生成器,但是一直没有时间。可以分享我的原理,就是分析网站的多个统计页面,发现 html 共性和差异,以此提取出网站页面的机构逻辑,实现模板自动生成、自适应等功能。以前在新闻页面抓取的时候,做过这方面东西,但是还不够通用。
    网页模板要和上面说的 grid 分布式系统结合,网页模板代码自动定期更新 /下发到抓取客户端。否则每次更新模板,还要去更新多个客户端程序很麻烦。

    先写这么多,下次写 lucene 和网站。
    第 2 条附言  ·  76 天前
    补交欠着的作业,今天把 Lucene 和网站部分介绍一下。

    基于之前的描述,假设我们已经完成了各个网站的文章抓取,那我们现在开始来做 Lucene 索引。

    1、首先,我们将之前抓取后文档存储的数据库表 Documents 增加一个字段,叫“ LuceneIndexStatus ”,用来标记每一篇文章所需要等待操作。具体可以定义如下:0=不需要 Lucene 操作,1=等待添加,2=等待更新,3=等待删除。
    2、写一个 Lucene 索引程序,可以是 Console 脚本,也可以是 exe 程序,都无所谓。程序通过搜索 Documents 表待操作的文档,将其添加 /更新 /删除到 Lucene 索引。我使用的 Lucene 版本没有更新功能,所以得先删除后添加。
    3、词库和分词:lucene 索引和查询要使用同一个分词系统和词库。关于词库网上的也不一定全,需要自己补充词库 ,我把地域、学校、城市、街道、小区、名人名字等添加到词库,另外写一个新词发现系统,用来生成新词自动添加到词库。新词发现系统的逻辑,就是运算大量的正文,基于标点符号和停用词分段后,如果几个字的排列组合还大量出现,则认定为新词。
    4、添加 Lucene Document 时候的 Trick:
    a )关于 Lucene.Net.Documents.Field.Store。为了前台检索性能考虑,将 Title、Description、URL 等关键字段设置成需要存储,这样当前台用户执行搜索时,执行了 Lucene 搜索后就能够直接获得这些信息,就不用再去数据库基于 ID 查匹配的 Title、Description 等信息了。这种做法,虽然 Lucene 索引做完后空间大了一点,但是查询性能会更好,毕竟减少了数据库 io。其他的一些辅助字段譬如 hostname (为了支持 insite 命令),就不需要存储了。
    b )建立 URL、Hostname 两个 Lucene 字段,而且设置成需要分词。URL 的目的是支持 inurl,hostname 的目的是支持 insite。在查询的时候可以查询 url 和 hostname,并给以一定的权重,譬如搜索 hadoop 的时候,hostname 为 hadoop.org 的文章会获得更高的权重。
    c) 在文章入 Lucene 库前,可以计算并设置一下文档的 boost 值,LuceneDocument.SetBoost(BoostValue)。计算文章 Boost 值可以基于:文章内容和标题的质量、详尽程度、网站权重、文章类型(博客、教程、还是新闻)等方法。建议在计算 boost 值时可以考虑使用 math.log10 函数,以较好的控制极大极小值,让 boost 值落在自己需要的区间。
    5、Lucene 索引完成后一定要 optimize,个人感觉最好 optimize 三次才有比较好的性能。
    6、要把 Lucene 操作 Index 的库和前台搜索的库分开,每次后台完成 Lucene 库 index 更新和 optimize 后,自动 copy 到前台的查询库,然后通知前台网站更换查询的 lucene 库。
    7、Lucene 库一旦 optimize 完成后,前台搜索不要更改这个库,否则产生碎片会影响查询性能。正确做法是,前台搜索指向两个或者多个 lucene 库。一个大库是定期更新的,可能有几百 G,由索引程序生成。还有若干个小库,是前台实时添加文档到索引,以支持查询使用的。前台 lucene 搜索时,搜索的是几个库的并集。
    第 3 条附言  ·  76 天前
    再来说说前台网站:

    这部分比较简单,就是从多个 lucene 库中查询文档并展示到前台。我就说说我自己的一些 trick 吧。
    1、IndexSearcher 访问 Lucene 大库,加上 readonly 标志,防止修改。为了性能,大库前台不动,新增临时文档由前台操作小库来完成。小库不一般可以全部放在内存。
    2、搜索建议使用 Lucene.Net.Search.ParallelMultiSearcher,有并较好的并行度,提升搜索性能。
    3、修改 Lucene 源码,来符合自己的查询、文章权重和排序需求,主要是这些文件:
    /Search/:TermScorer,TermQuery,IndexSearcher,BooleanQuery
    4、自己重载 Lucene.Net.Search.Similarity,修改文档算分。这个貌似起到的作用不是很大,没有上面修改源码来的效果好。
    5、为了提升性能,lucene 索引库至少要放在 ssd 上。为了提升性能,搜索缓存很重要,一个是调整 lucene 自己的缓存机制,另一个方面自己的前台程序可以缓存搜索结果集。缓存搜索结果集的时候,不要只缓存当前页的搜索结果,否则翻页还是慢。建议每 10 页一缓存,这样在搜索分页间跳转的时候就会很快。

    暂时就这些,最近其他杂事太多,在忙着处理,有些细节记得不是特别清楚了,所以只能写个大概,大家见谅。
    109 回复  |  直到 2019-08-31 09:55:15 +08:00
    1  2  
        101
    orangeChu   63 天前
    忘说了我的技术栈是 nodejs,有相关快速检索的方法吗?望不吝赐教,先谢过~
        102
    orangeChu   63 天前
    忘说了,我的技术栈是 nodejs,有相关快速检索的方式吗?望不吝赐教,先谢过~
        103
    nyfok   63 天前   ♥ 1
    @orangeChu 我理解的通讯录数据就是姓名加电话号码,搜索方式就是:电话号码搜索(数字包含关系)、姓名搜索(全拼搜索、汉字或英文字母搜索、拼音首字母搜索)。数据如果只有几千条的话,我觉得不应该慢。
    用 lucene 对这个搜索帮助不大,应为姓名和电话号码没有什么好分词的,既然用不到分词,这样 lucene 的搜索效果就体现不出来。当然,你可以使用单字分词,即一个字符作为一个分词,这样也可以,只需要自己按照 lucene 规范写一个分词器即可。
    我个人建议,你可以试试看把这些记录全部加载到内存进行检索,速度应该很快,几千条数据应该没问题,这个方案还更简单。如果速度还慢,可以在内存里面写多个 set 集,参照 lucene 的原理。
    譬如,张三,张四,张五,张张
    =》如下 set 集:
    zhang:张三(0),张四(0),张五(0),张张(0|1)
    san:张三(1)
    si: 张四(1)
    wu: 张五(1)
    搜索时根据输入首先获得对应的 set 集,对多个 set 集进行交集操作即可获得符合的记录。以上是最简单 demo,具体你可以设计算法,以更加符合自己的需求。
        104
    orangeChu   63 天前
    @nyfok 好的,按照楼主提供的思路我再想想,再次感谢楼主写的一大段剖析 :)
        105
    nyfok   63 天前
    @orangeChu 不客气啊,祝一次调通
        106
    dirkl   52 天前 via Android   ♥ 1
    最近用了一下,貌似比以前好很多了,楼主升级了?
        107
    nyfok   51 天前
    @dirkl 感谢支持!之前因为随着索引结果集越来越大,所以导致查询速度有所变慢,最近优化了一下查询这一块,提高了速度,所以效果更好了。
        108
    qdl   35 天前
    @nyfok 想请教楼主你搜索引擎的分页功能是如何实现的,lucene 分页。
        109
    nyfok   19 天前
    @qdl lucene 搜索出来是类似于 list 的一个对象,通过页面大小(一个页面多少条记录)和页码,就可以计算出每页的起始和结束文档序号,从 lucene 搜索的 docs 对象里面直接按照序号取就行了。但是为了性能,不能每翻一页就 lucene 查询一下,所以可以在 lucene 上面封装一层 cache 层,每十页一 cache,这样十页内 lucene 其实只查询了一次,速度更快。
    1  2  
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3204 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 23ms · UTC 04:56 · PVG 12:56 · LAX 21:56 · JFK 00:56
    ♥ Do have faith in what you're doing.