用Lucene构造垂直搜索引擎

Lucene是用于全文检索的开源库,Apache软件基金会提供支持。它由Java语言开发,也提供Python接口调用。

本文介绍使用开源项目Lupyne构建垂直搜索引擎,搜索本地网页中的内容。它使用Python语言编写,搜索功能用Lucene引擎实现,使用html2text从本地网页中提取数据,实现对网页中文本的搜索,前端调用CherryPy框架(flask的web server常用作开发测试。而cherrypy的web server常用于生产环境),提供网页搜索功能。

运行文中实例需要匹配Java,Python,Lucene等各个软件版本,环境配置比较复杂,因而基于Lucene提供的docker image环境构建。

Lucene元素

使用Lucene之前,先来了解一些相关概念。

Directory:指定如何保存构建的索引,测试时常保存在内存中,实际应用中,一般将其保存在文件系统中,本例将索引保存在/tmp/a目录下。
Analyzer:分析器,用于处理文本,如分词,去停用词等。
IndexWriter:对索引进行增删查改的工具,一般的操作都围绕IndexWriter展开。
Document:构造搜索的基本单位,一般是网页、文档、邮件等。
Field:一个Document可能包含多个域Field,比如标题、正文、作者等。

实例

下面介绍垂直搜索引擎的具体构建方法。

下载Lupyne源码

$ git clone [https://github.com/coady/lupyne](https://github.com/coady/lupyne) 

下载docker镜像(在Linux系统中运行) $ docker pull coady/pylucene # 约2.52G

运行镜像

$ docker run --rm -v /exports:/exports --net=host -it coady/pylucene bash

启动docker时使用了--net=host,使docker内外使用相同端口号访问,-v将宿主机目录映射到镜像内部目录。

在镜像内安装支持软件

$ cd /exports/xxx # 切换到lupyne源码所在目录
$ python setup.py install
$ pip install cherrypy
$ pip install pytest
$ pip install clients
$ pip install jupyter # 供进一步开发使用
$ pip install html2text # 解析html内容

运行jupyter开发环境:

$ jupyter notebook --allow-root -y --no-browser --ip=0.0.0.0

在docker之外的浏览器中访问示例文件:http://localhost:8888/notebooks/docs/examples.ipynb,其中对比使用lupyne封装与直接使用pylucene分别建立索引、查询、检索、排序和分组的不同代码,可直接运行。

读取目录下的网页内容,写入索引数据:

import lucene
from lupyne import engine
from org.apache.lucene import analysis, document, index, queryparser, search, store
import os
import html2text

def parse_html(root_dir, path, indexer, debug=False):
    f = open(path,"r")
    content = f.read()
    text = html2text.html2text(content)
    strings = [i for i in text.split("\n") if len(i) > 0]
    for i in strings:
        if debug:
            print(len(strings), "insert", i)
        path = path.replace(root_dir, "http://localhost:8080/")
        indexer.add(content=i, path=path)
    indexer.commit()

def insert_dir(root_dir, book_dir):
    html_dir = os.path.join(root_dir, book_dir)
    for root, dirs, files in os.walk(html_dir):
        for f in files:
            if f.endswith("html") or f.endswith("htm"):
                path = os.path.join(root, f)
                print(path)
                parse_html(root_dir, path, indexer)
                
def search(keyword):
    hits = indexer.search(keyword, field='content')
    for hit in hits:
        print(hit)

if __name__ == '__main__':
    BOOK_ROOTDIR = '/exports/books/contents/' # 数据根目录,与Lucene中配置文件一致
    assert lucene.getVMEnv() or lucene.initVM()
    indexer = engine.Indexer(directory="/tmp/a/") # 数据存储目录
    indexer.set('content', engine.Field.Text, stored=True)  
    indexer.set('path', engine.Field.Text, stored=True)  
    # 插入
    insert_dir(BOOK_ROOTDIR, '知识图谱/text')
    # 搜索
    search('Lucene')
    indexer.close()

设置Lupyne的配置文件:

[global]
tools.staticdir.on=True
tools.staticdir.dir='/exports/books/contents/'
tools.staticdir.section=''

启动Lupyne服务

$ python -m lupyne.server /tmp/a -c a.cfg

搜索:在浏览器打开 http://localhost:8080/search?q=content:lucene 如只取前三个使用: http://localhost:8080/search?q=content:lucene&count=3

打开被搜索网页: http://localhost:8080/知识图谱/text/part0013_split_006.html

此时,就实现了搜索本地网页的最简版本垂直搜索引擎,尽管网页还比较简陋。

扩展

Elasticsearch (ES)是一个开源的搜索引擎,建立在Lucene基础之上。 Lucene可能是目前存在的,不论开源还是私有的,拥有最先进,高性能和全功能搜索引擎功能的库。但是 Lucene 仅仅只是一个库,相对来说ES+Kibana更像是一套相对成熟的方案。它提供Web服务,客户端可通过Http请求或者API接口增删查改,它相对于Lupyne更复杂,功能也更多。当然无论使用何种底层库,都需要自己解释和插入数据。