您的位置:首页 > 编程语言 > Python开发

python搜索引擎之搜索系统的建立——根据关键字命中次数排分给出前若干个答案

2015-11-13 00:09 696 查看
根据前面的博客建立了如下的各种索引库的表:

1.词典(对每个关键字分配一个 wordId)

词典放在命名为 backwardTableDb.db 中的 wordIdTable 中,见部分字典截图如下:





2.建立后向索引,及相关表项

后向索引有两个表项,其中第一个表项是后向索引表,其指定每个wordId,以及出现过该关键字的doc 的数量,同时还有该关键字的 doc hits 在后面一个表中的偏移,后一个表是后向表,记录了docId,以及对应当前关键字的hitlist,关于后向索引表的具体内容,百度吧。。实现是为了根据偏移快速定位到对应的docId的位置,人为增加了一列 docIdIndex项,两个表如下:





上述两个表的关系是:第一个表中的 wordId 出现过的网页的编号(docId)的连续存在第二个表中,第一个表中 offset 指出了存储编号的起始位置,该位置接下来的 nDocs个表项都是对应 wordId 出现过的 wordId

3.基本思想描述:

对输入的搜索语句,先进行分词,对每个关键字,根据后向索引表,找到对应的命中 docId 的 set,综合所有set评估docId,最后哪个docId 中命中的关键字个数越多,该docId 对应的网页越符合搜索需求。

4.代码段解释

4.1 建一个类来存储后向索引找到的对象,记录 doc_id,nhits,以及 hitList(字符串,分别记录命中的起始与结束位置)

class BackwardObj:
    doc_id = 0
    nhits = 0
    hit_list = ''
    
    def __init__(self,t_doc_id,t_nhits,t_hit_list):
        self.doc_id = t_doc_id
        self.nhits = t_nhits
        self.hit_list = t_hit_list[:]  # Python 中 list 与 dictionary 均是引用,避免此处引用




4.2 定义一个中枢类,用来控制数据库的链接,存储词典

class CentralControl:
    conn = sqlite3.connect("backwardTableDb.db")  #conn 控制后向数据库
    cursor = conn.cursor()
    
    org_conn = sqlite3.connect("orgDb.db")   #org_conn 控制原始数据库,用于在找到结果 docId 后,找出标题内容
    org_cursor = org_conn.cursor()
    
    word_id_dic = {}<span style="white-space:pre">	</span>#词表,内容是 {关键字:关键字Id}
    
    def __init__(self):
        self.loadWordIdDic()  #构造函数内读入词典
    
    def loadWordIdDic(self):
        self.cursor.execute("SELECT * FROM wordIdTable") # SQL 语法抓出词典表内所有内容
        word_id_data = self.cursor.fetchall()
        
        for word_id_obj in word_id_data: 
            self.word_id_dic[word_id_obj[0]] = word_id_obj[1]  # 将抓出的每个内容放入词典中


4.3 定义查找函数,以下所有函数均是中枢类的成员函数,(为了能快速访问词典并使用数据库连接)

def oneSearch(self,query):                  # query 存储搜索字符串
        query = query.lower()                # 若存在大写字符,转到小写
        seg_list = jieba.cut_for_search(query)  #使用 jieba 分词,将搜索字符串进行分词
        seg_list = list(seg_list) 		# 将分词结果放在 List 中
        if ' ' in seg_list:   #deal ' '
            seg_list = [i for i in seg_list if i != ' '] # 将关键字中的空格字符去掉,注意 seg_list.remove(" ")仅能删除第一个空格
       
<span style="white-space:pre">	</span># 建立一个字典。{关键字:[每个命中的表项形成一个BackwardObj 对象(上面定义),多个对象列表]}
	set_dic = {}				
        for key_word in seg_list:
            current_set = self.getDocSet(key_word)  #调用函数找到该关键字对应的 BackwardObj 对象列表(根据后向表查找),具体定义见下
            if len(current_set) != 0 :<span style="white-space:pre">		</span> # 若找到了该关键字对应的对象列表
                set_dic[key_word] = current_set  # 将该关键字与列表加入上面定义的关键字为键值的字典中
         <pre name="code" class="python"><span style="white-space:pre">	</span># 根据所有关键字及其对应的命中 docId set 的综合,建立一个 list,里面存储的是一个个元组,其中第一个元素是 docId,第二个元素是该 docId 命中的关键字的个数,且该 list 是按命中关键字个数从高到低排序的。具体代码见下。
doc_set = self.getDocIdScores(set_dic) if len(doc_set) != 0: self.printDocInfo(doc_set) # 否则,打印找到的信息,具体代码见下 else: #若一个对应页面均没有找到,则说明没有找到相关信息 print "not find information"



4.4 根据关键字,获取该关键字在后向表中对应表项组成的对象列表(getDocSet)

(1)getDocSet

def getDocSet(self,key_word):
        current_set = []<pre name="code" class="python"><span style="white-space:pre">	</span>
<span style="white-space:pre">	</span># 调用函数找到该关键字在后向索引表中的表项值,即命中 doc 数与命中信息表项的偏移。具体见下
[ndocs,offset] = self.findKeyWordDocId(key_word) if ndocs == 0 and offset == 0 : # 若当前关键字不在字典中 return current_set # 返回空列表 current_set = self.getSetFromBT(ndocs,offset) # 根据后向索引表中的信息,去后向表中寻找对应表项,并建立
BackwardObj 列表。具体见下 return current_set



(2)findKeyWordDocId

def findKeyWordDocId(self,key_word):
        ndocs_offset = []
        if not self.word_id_dic.has_key(key_word):   # 若当前关键字不在字典中,则返回 ndocs = 0,offset = 0,协助 getDocSet 判断
            ndocs_offset.append(0)
            ndocs_offset.append(0)
            return ndocs_offset
        word_id = self.word_id_dic[key_word]         # 根据字典获取当前关键字的 wordId
        <pre name="code" class="python" style="font-size:14px;"><span style="white-space:pre">	</span># 使用SQL 语法抓出当前 wordId 在后向索引表中的对应表项

self.cursor.execute("SELECT * FROM backwardIndexTable WHERE wordId = (?)",(word_id,)) #过滤表项

info = self.cursor.fetchone() #抓出该表项 ndocs_offset.append(info[1]) # 将表项中的第一项,即 ndocs 放入返回列表的第一项 ndocs_offset.append(info[2])
# 将 offset 放入返回列表的第二项 return ndocs_offset



(3)getSetFromBT

def getSetFromBT(self,ndocs,offset):
        current_set = []
        doc_id_index_beg = offset + 1  # 算出偏移量对应的 docIdIndex 的起始值,即为什么要加一项 docIdIndex
        doc_id_index_end = doc_id_index_beg + ndocs - 1  # 算出偏移量对应的 docIdIndex 的结束位置
        <pre name="code" class="python"><span style="white-space:pre">	</span># 根据SQL 语法,抓出对应所属位置的表项

self.cursor.execute("SELECT * FROM backwardTable WHERE docIdIndex between (?) and (?)",(doc_id_index_beg,doc_id_index_end)) total_info = self.cursor.fetchall(); for info in total_info: current_set.append(BackwardObj(info[1],info[2],info[3])) # 解析每一个表项并建立对应的每个
BackwardObj 对象,将其加入 list中 return current_set



4.5 对每个关键字命中的 docId 进行统计,统计每个出现过关键字的 docId 命中关键字的个数,并按照命中个数由多到少进行排序

def getDocIdScores(self,set_dic):
        doc_score_dic = {} # 建立字典,键是 docId,而值是该 docId 命中关键字的个数,以下简称doc命中字典 
        for key in set_dic: # 遍历关键字及命中 doc 序列组成的字典
            for obj in set_dic[key]:  # 对每个关键字,遍历命中该关键字的表项构成的对象 BackwardObj
                if doc_score_dic.has_key(obj.doc_id): # 为该 docId 的命中次数累加 
                    doc_score_dic[obj.doc_id] += 1
                else:
                    doc_score_dic[obj.doc_id] = 1
        doc_score_set = sorted(doc_score_dic.iteritems(),key = lambda d : d[1],reverse = True)  # 根据doc 命中字典中的命中次数进行排序
        return doc_score_set


4.6 根据 doc 命中字典显示查询结果:

def printDocInfo(self,doc_set):
        index = 0
        while index < 8:  //默认返回命中关键字个数最多的前八个 title
            doc_id = doc_set[index][0]
            hit_times = doc_set[index][1]
            index += 1
            self.org_cursor.execute("SELECT title, url FROM orgTable WHERE id == (?)",(doc_id,))
            info = self.org_cursor.fetchone()
            
            print "title : ",info[0]
            print "url : ",info[1]
            print "hits : ",hit_times
            print "------------------------------------"

4.7 上面所有函数均定义为中枢类的成员函数,main函数中的调用代码如下:

if __name__ == "__main__":
    central_control = CentralControl()
    while True:
        query = raw_input("input query:")
        central_control.oneSearch(query)

    central_control.conn.close()
    central_control.org_conn.close()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: