写学习笔记是我学习python以来养成的一个习惯,每学习一个知识点,便整理成文字记录下来。搜索引擎大家经常都有在使用,国内外也很很多搜索引擎平台。

Google搜索引擎建立至今已经快20年了,之后全球各类大大小小类似的搜索引擎也陆续出现、消亡。国内目前以百度为大,搜狗、360、必应等也势在必争。搜索引擎技术也发展的相当成熟,同时也就出现了很多开源的搜索引擎系统。比如,Solr、Lucene、Elasticsearch、Sphinx等。

本文以sphinx search为例来介绍如何打造自己的搜索引擎。该搜索引擎的架构大致如下:

Sphinx search

Sphinx search 是俄罗斯人用C++写的,速度很快,可以非常容易的与SQL数据库和脚本语言集成,内置MySQL和PostgreSQL 数据库数据源的支持。其官方网站是: http://sphinxsearch.com/

可以说Sphinx支持包括英文、中文等所有语言的搜索。英文是以空格、标点符号来分割单词的,很容易切分。而中文词汇之间是没有空格的,很难区分,所以才有了自然语言处理中的“中文分词”技术的研究。Sphinx默认把中文按字拆分的,但这样就会产生搜索出不相干的内容来。比如,搜索“中国”,它会把同时包含“中”和“国”但不包含“中国”的文档搜出来。因此,有人就给Sphinx打了中文分词的补丁。

如果没有搞错的话,最早添加中文分词的是Coreseek,好像也是中文圈用得最广的支持中文分词的Sphinx,其它还有sphinx-for-chinese。然而这二者基于的Sphinx版本都太低了,有好多年没有更新。其中存在的一些Sphinx的bug也没有解决。

github上有一个基于Sphinx 2.2.9版本的代码库添加了中文分词: https://github.com/eric1688/sphinx  经测试,该版本稳定性和速度都要好于coreseek。当然它依然支持英文等其它语言的搜索,只是对中文搜索更加准确了。

Sphinx 安装

  1. git clone https://github.com/eric1688/sphinx
  2. cd sphinx
  3.  
  4. #编译(假设安装到/usr/local/sphinx目录,下文同)
  5. ./configure --prefix=/usr/local/sphinx
  6. # 说明: --prefix 指定安装路径 --with-mysql 编译mysql支持 --with-pgsql 编译pgsql支持
  7. make
  8. sudo make install

安装好后,在/usr/local/sphinx目录下有以下几个子目录:
etc/  sphinx配置文件,不同的索引可以写不同的配置文件
bin/  sphinx程序,其中有建立索引的程序:indexer, 搜索守护进程:searchd
var/  一般用了放置indexer索引好的文件

Sphinx索引的建立

MySQL数据库表结构 
从上面的架构图可以看出来,我们要搜索的数据都存放在MySQL数据库中。假设我们的数据库名称叫blog_data,其中有个表叫article,表结构如下:

字段名 说明
id 文章唯一id(主键)
title 文章标题
content 文章内容
created_time 文章创建时间

该article表可以是你本身网站的文本内容存放的表格,也可以是你的网络爬虫抓取到的数据存储表。

还有建立另外一个表sph_counter用来存储indexer已经索引的最大doc id

字段名 说明
counter_id 标记是对哪个表做记录
max_doc_id 被索引表的最大ID
note 注释,可以是表名
update_at 更新时间

建立索引配置文件:  
新建或修改/usr/local/sphinx/etc/blog.conf 配置文件:

  1. source blog_main
  2. {
  3. type = mysql
  4. sql_host = localhost
  5. sql_user = reader
  6. sql_pass = readerpassword
  7. sql_db = blog_data
  8. sql_port = 3306
  9. sql_query_pre = SET NAMES utf8mb4
  10. sql_query_pre = REPLACE INTO sph_counter SELECT 1, MAX(id), 'article', NOW() FROM article
  11.  
  12. sql_query = SELECT id, title, content, \
  13. UNIX_TIMESTAMP(created_time) AS ctime, \
  14. FROM article \
  15. WHERE id <= (SELECT max_doc_id from sph_counter WHERE counter_id=1)
  16. sql_attr_timestamp = ctime #从SQL读取到的值必须为整数,作为时间属性
  17.  
  18. }
  19.  
  20. index blog_main
  21. {
  22. source = blog_main #对应的source名称
  23. path = /user/local/sphinx/var/data/blog_main
  24. docinfo = extern
  25. mlock = 0
  26. morphology = none
  27. min_word_len = 1
  28. html_strip = 0
  29.  
  30. charset_type = utf-8
  31. chinese_dictionary = /user/local/sphinx/etc/xdict #中文分词的词典
  32. ngram_len = 0
  33. stopwords = /user/local/sphinx/etc/stop_words.utf8
  34. }
  35.  
  36. #全局index定义
  37. indexer
  38. {
  39. mem_limit = 512M
  40. }
  41.  
  42. #searchd服务定义
  43. searchd
  44. {
  45. listen = 9900
  46. listen = 9306:mysql41 # 实时索引监听的端口
  47. read_timeout = 5
  48. max_children = 90
  49. max_matches = 100000
  50. max_packet_size = 32M
  51. read_buffer = 1M
  52. subtree_docs_cache = 8M
  53. subtree_hits_cache = 16M
  54. #workers = threads•
  55. dist_threads = 2
  56. seamless_rotate = 0
  57. preopen_indexes = 0
  58. unlink_old = 1
  59. pid_file = /usr/local/sphinx/var/log/blog_searchd_mysql.pid
  60. log = /usr/local/sphinx/var/log/blog_searchd_mysql.log
  61. query_log = /usr/local/sphinx/var/log/blog_query_mysql.log
  62. }

编辑好以上配置文件,就可以开始建立索引了:

  1. cd /usr/local/sphinx/bin
  2. ./indexer -c ../etc/blog.conf
  3. # 如果已经有searchd在运行了,就要加 --roate 来进行索引

索引建立后,就会在var/data/下面有名称前缀为blog_main.XXX的索引文件生成。

建立实时索引 
上面的配置文件是建立一个静态索引,把当时数据库里面的所有数据进行索引。但是,你的数据库往往是不断增加新数据的。为了及时索引并搜索到最新加入的数据,就需要配置实时索引了。

  1. index rt_weixin {
  2. type = rt
  3. path = /usr/local/sphinx/var/data/rt_blog
  4. rt_field = title
  5. rt_field = content
  6.  
  7. rt_attr_timestamp = pubtime
  8. ngram_chars = U+3000..U+2FA1F #为了支持中文
  9. ngram_len = 1
  10. }

该仓库代码的作者可能是忘了给实时索引加中文分词,如果不配置ngram_chars 参数就不能搜到中文,添加后搜索是按单字匹配的,可见作者确实是忘了给实时索引部分加中文分词。

添加以上实时索引后并不能搜索到实时数据。实时索引的更新/添加只能通过SphinxQL(一个类似MySQL的协议),所以还要写一个Python脚本,从数据库读取最新的数据并通过SphinxQL更新到实时索引。

  1. import MySQLdb
  2. # 连接实时索引
  3. db_rt = MySQLdb.connect(
  4. '127.0.0.1',
  5. 'nodb', # 对于实时索引来说,db,user,password都是不需要的,随便写。
  6. 'noname',
  7. 'nopass',
  8. port=9306, # 实时索引监听的端口
  9. )
  10.  
  11. # 向实时索引更新数据的函数
  12. def into_rt(index_name, item):
  13. cursor = db_rt.cursor()
  14. fields = item.keys()
  15. values = item.values()
  16. fieldstr = ','.join(fields)
  17. valstr = ','.join(["'%s'"] * len(item))
  18. for i in xrange(len(values)):
  19. if isinstance(values[i], unicode):
  20. values[i] = values[i].encode('utf8')
  21. elif isinstance(values[i], datetime):
  22. try:
  23. values[i] = int(time.mktime(values[i].timetuple()))
  24. except:
  25. traceback.print_exc()
  26. print values[i]
  27. values[i] = int(time.time())
  28. sql = 'INSERT INTO %s (%s) VALUES(%s)' % (index_name, fieldstr, valstr)
  29. # print sql
  30. sql = sql % tuple(values)
  31. try:
  32. cursor.execute(sql)
  33. db_rt.commit()
  34. except Exception, e:
  35. if e[0] == 1064:
  36. # ignore duplicated id error
  37. pass
  38. else:
  39. traceback.print_exc()
  40. raise 'realtime index error'
  41. finally:
  42. cursor.close()

以上是及时建立实时索引的python程序的主要部分。可以把它设置成后台一直运行的守护程序,也可以在crontab里面配置每隔几分钟运行一次。

索引的更新 
静态的主索引如果只建立一次,实时索引的数据量会越积越多,对实时索引的搜索带来很大压力,所以我们要定时重新建立主索引,清理实时索引。
清理实时索引的程序可以参考上面建立实时索引的python程序。

  • crontab 设置每天凌晨1点运行 indexer
  • crontab 设置indexer运行完毕后清理实时索引,并从新的max_doc_id开始建立实时索引

以上就是建立一个自己的搜索引擎的过程。更多配置细节可到官方网站参考文档。

以下笔记整理于猿人学

python学习笔记:建立一个自己的搜索引擎的更多相关文章

  1. Python学习笔记_一个Tkinter示例,使用FileDialog

    为了使用Python进行数据分析,编写一个图形界面,选择一个Excel文件(或CSV),然后进行后续处理. 一.本示例涵盖如下知识点: 1.FileDialog的使用 2.退出程序 3.消息提示框的示 ...

  2. 07 python学习笔记-写一个清理日志的小程序(七)

    #删掉三天前的日志 #1.获取到所有的日志文件, os.walk #2.获取文件时间 android 2019-09-27 log,并转成时间戳 #3.获取3天前的时间 time.time() - 6 ...

  3. python 学习笔记 12 -- 写一个脚本获取城市天气信息

    近期在玩树莓派,前面写过一篇在树莓派上使用1602液晶显示屏,那么可以显示后最重要的就是显示什么的问题了. 最easy想到的就是显示时间啊,CPU利用率啊.IP地址之类的.那么我认为呢,假设可以显示当 ...

  4. OpenCV之Python学习笔记

    OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...

  5. 【python学习笔记】4.字典:当索引不好用时

    [python学习笔记]4.字典:当索引不好用时 字典是python中唯一内建的map类型 创建: key可以为任何不可改变的类型,包括内置类型,或者元组,字符串 通过大括号: phonebook={ ...

  6. [Python学习笔记]正则表达式总结

    常用缩写字符及其含义表格查询 缩写字符分类 含义 \d 0-9的任意数字 \D 除0-9的数字以外的任何字符 \w 任何字母.数字或下划线字符(可以认为是匹配"单词"字符) \W ...

  7. VS2013中Python学习笔记[Django Web的第一个网页]

    前言 前面我简单介绍了Python的Hello World.看到有人问我搞搞Python的Web,一时兴起,就来试试看. 第一篇 VS2013中Python学习笔记[环境搭建] 简单介绍Python环 ...

  8. python学习笔记之module && package

    个人总结: import module,module就是文件名,导入那个python文件 import package,package就是一个文件夹,导入的文件夹下有一个__init__.py的文件, ...

  9. python学习笔记(六)文件夹遍历,异常处理

    python学习笔记(六) 文件夹遍历 1.递归遍历 import os allfile = [] def dirList(path): filelist = os.listdir(path) for ...

随机推荐

  1. PostgreSQL学习笔记——窗口函数

    在学习窗口函数之前,我们新建一个Product表并往其中插入一些数据: drop table if exists Product; create table Product ( product_id ...

  2. AWS 云上安全最佳实践

    目录 一.账号及访问管理 1.1.多 VPC 还是多账号模式 1.2.多账户模式,选择主 master 账号 二.系统架构安全 2.1.子网建议 2.2.每个可用区子网划分 2.3.安全组的建议 2. ...

  3. Egret入门学习日记 --- 第二十篇(书中 9.1~9.3 节 内容 组件篇)

    第二十篇(书中 9.1~9.3 节 内容 组件篇) 第八章中的内容. 以上都是基本的Js知识,我就不录入了. 直接来看 第9章. 开始 9.1节. 以上内容告诉你,Egret官方舍弃了GUI,使用了E ...

  4. 【miscellaneous】基于gstreamer的实时转码

    目标是实现一个实时转码,可用于IPTV提供节目源.相关工作在ubuntu操作系统下进行.需要对源代码进行修改的时候,直接采用apt-get source命令获取源代码,根据需要进行修改,然后安装,这样 ...

  5. java spring事务管理相关

    一般项目结构为: 数据持久层dao     业务层service     控制层controller 事务控制是在业务层service起作用的,所以需要同时对多张表做添加,修改或删除操作时应该在ser ...

  6. JS 报错(intermediate value)(...) is not a function

  7. vim文本编辑器的介绍及使用

    (一)什么是vim编辑器 在Linux系统中配置应用服务,实际上就是在修改它的配置文件(配置文件可能有多个,其中包含不同的参数),而且日常工作中也一定免不了编写文档的事情吧,这些都是要通过文本编辑器来 ...

  8. 使用Cython时遇到的cl.exe的问题

    最近使用cython时,遇到一个问题,报错如下: cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD "-Id:\program files\python3 ...

  9. springboot使用activemq同时接收queue和topic消息

    原文链接:https://blog.csdn.net/jia_costa/article/details/79354478 新建springboot项目, pom文件如下 <?xml versi ...

  10. 《Tsinghua os mooc》第11~14讲 进程和线程

    第十一讲 进程和线程 进程 vs 程序 程序 = 文件 (静态的可执行文件) 进程 = 执行中的程序 = 程序 + 执行状态 进程的组成包括程序.数据和进程控制块 同一个程序的多次执行过程对应为不同进 ...