搜索与排名思路整理

学习《集体智慧编程》第4章的思路整理:

本章的主要内容就是建立一个模拟的全文搜索引擎,主要的大步骤为:1.检索网页,2.建立索引,3.对网页进行搜索 4.多种方式对搜索结果进行排名

一、检索网页:主要利用python写了一个爬虫软件,通过爬取一个网站上链接来不断的扩充爬取的内容。主要利用了python的urllib库和BeautifulSoup库。这部分比较简单,核心代码如下:

def crawl(self,pages,depth=2):
        for i  in range(depth):
            newpages=set()
            for page in pages:
                try:
                    c=request.urlopen(page)
                except:
                    print(‘Could not open %s‘ %page)
                    continue
                soup=BeautifulSoup(c.read(),‘html5lib‘)
                self.addtoindex(page,soup)

                links=soup(‘a‘)
                for link in links:
                    if (‘href‘ in dict(link.attrs)):
                        url=urljoin(page,link[‘href‘])
                        if url.find("‘")!=-1:
                            continue
                        url=url.split(‘ ‘)[0]#去掉位置信息
                        if url[0:4]==‘http‘ and not self.isindexed(url):
                            newpages.add(url)
                        linkText=self.gettextonly(link)
                        self.addlinkref(page,url,linkText)
                self.dbcommit()
            pages=newpages    ####位置!!!将一个网页里面的连接保存起来

这里采用了广度优先搜索方式

二、建立索引

1.sqlite数据库的使用:

在python2.x版本中需要自己下载安装这个数据库,但是在python3.x中我们只需要 import sqlite3即可。首先需要修改类的一些内建函数:

###初始化crawler类并且传入数据库名称
    def __init__(self,dbname):
        self.con=sqlite3.connect(dbname)
    def __del__(self):
        self.con.close()
    def dbcommit(self):
        self.con.commit()

然后建立数据库,这里首先需要准备5张数据库表,分别为:urllist(保存的是已经过索引的url列表)、wordlist(保存的是单词列表)、wordlocation(保存的是单词在文档中的所处的位置列表)、link(保存两个Url ID 指明从一个表到另一张表的连接关系) linkwords(利用了字段wordid和linkid记录了哪些单词和链接实际相关),并且建立一些索引完整的数据库schema如下图:

具体代码如下:

def createindextables(self):
        # self.con.execute(‘drop table if exists urllist,wordlist,wordlocation,link,linkwords‘)
        self.con.execute(‘create table urllist(url)‘)
        self.con.execute(‘create table wordlist(word)‘)
        self.con.execute(‘create table wordlocation(urlid,wordid,location)‘)
        self.con.execute(‘create table link(fromid INTEGER ,toid INTEGER )‘)
        self.con.execute(‘create table linkwords(wordid,linkid)‘)
        self.con.execute(‘create index wordidx on wordlist(word)‘)
        self.con.execute(‘create index urlidx on urllist(url)‘)
        self.con.execute(‘create index wordurlidx on wordlocation(wordid)‘)
        self.con.execute(‘create index urltoidx on link(toid)‘)
        self.con.execute(‘create index urlfromidx on link(fromid) ‘)
        self.dbcommit()

2.在网页中查找单词,此处还是利用了BeautifulSoup的强大功能,将网页中的所有的文字信息提取出来,去掉一些标签,属性之类的。然后进行单词的分词。在这里的单词分词都是针对英文单词的,关于中文单词的分词,有个名为jieba的库可以实现。感兴趣的可以看一下:https://github.com/fxsjy/jieba/tree/jieba3k

 ##从一个Html网页中抓取文字(不带标签的)
    def gettextonly(self,soup):
        v=soup.string
        if v==None:
            c=soup.contents  ####把content是错误的写成了content
            resulttext=‘‘
            for t in c:
                subtext=self.gettextonly(t)
                resulttext+=subtext+‘\n‘
            return resulttext
        else:
            return v.strip()

    ###根据任何非空白字符进行分词处理
    def separatewords(self,text):
        ##该方法仅仅局限于分英文单词
        splitter=re.compile(‘\\W*‘)
        return [s.lower() for s in splitter.split(text) if s!=‘ ‘]
        ###中文的应用了jieba库,关于该库可以参见https://github.com/fxsjy/jieba/tree/jieba3k
        # return (list(jieba.cut_for_search(text))) ##从一个Html网页中抓取文字(不带标签的)
    def gettextonly(self,soup):
        v=soup.string
        if v==None:
            c=soup.contents  ####把content是错误的写成了content
            resulttext=‘‘
            for t in c:
                subtext=self.gettextonly(t)
                resulttext+=subtext+‘\n‘
            return resulttext
        else:
            return v.strip()

    ###根据任何非空白字符进行分词处理
    def separatewords(self,text):
        ##该方法仅仅局限于分英文单词
        splitter=re.compile(‘\\W*‘)
        return [s.lower() for s in splitter.split(text) if s!=‘ ‘]
        ###中文的应用了jieba库,关于该库可以参见https://github.com/fxsjy/jieba/tree/jieba3k
        # return (list(jieba.cut_for_search(text)))

3、加入索引,将网页以及所有的单词加入索引,在网页和单词之间建立关联,并且保存单词在文档中的位置。在本例中,文档的位置就是在列表中的索引号

 ###为每个网页建立索引
    def addtoindex(self,url,soup):
        if self.isindexed(url):
            return
        print ("Indexing:"+url)

        ##获取每个单词
        text=self.gettextonly(soup)
        words=self.separatewords(text)

        ##得到URL的id
        urlid=self.getentryid(‘urllist‘,‘url‘,url)
        ##将每个单词与该url相关联
        for i in range(len(words)):
            word=words[i]
            if word in ignorewords:
                continue
            wordid=self.getentryid(‘wordlist‘,‘word‘,word)####此处出现错误
            self.con.execute("insert into wordlocation(urlid,wordid,location) values (%d,%d,%d)" % (urlid, wordid, i)

三、查询

 def getmatcheows(self,q):
        ###构造查询的字符串
        fieldlist=‘w0.urlid‘
        tablelist=‘‘
        clauselist=‘‘
        wordids=[]

        ##根据空格拆分单词
        words=q.split(‘ ‘)
        print(words)
        tablenumber=0

        for word in words:
         # Get the word ID
            wordrow=self.con.execute(
            "select rowid from wordlist where word=‘%s‘" % word).fetchone()
            if wordrow!=None:
                wordid=wordrow[0]
                wordids.append(wordid)
                if tablenumber>0:
                    tablelist+=‘,‘
                    clauselist+=‘ and ‘
                    clauselist+=‘w%d.urlid=w%d.urlid and ‘ % (tablenumber-1,tablenumber)
                fieldlist+=‘,w%d.location‘ % tablenumber
                tablelist+=‘wordlocation w%d‘ % tablenumber
                clauselist+=‘w%d.wordid=%d‘ % (tablenumber,wordid)
                tablenumber+=1

        # Create the query from the separate parts
        fullquery=‘select %s from %s where %s‘ % (fieldlist,tablelist,clauselist)
        print (fullquery)
        cur=self.con.execute(fullquery)
        rows=[row for row in cur]

        return rows,wordids

四、排名

1.基于内容的排名:评价度量包括以下三种:单词频度,文档位置,单词距离

首先我们要设定一个归一化函数,对结果进行归一化处理,使得他们具有相同的值域和变化方向。

###归一化函数 返回一个介于0~1之间的值
    def normalizescores(self,scores,smallIsBetter=0):
        vsmall=0.00001  #避免被零整除
        if smallIsBetter:
            minscore=min(scores.values())
            return dict([(u,float(minscore)/max(vsmall,l)) for (u,l) in scores.items()])
        else:
            maxscore=max(scores.values())
            if maxscore==0:maxscore=vsmall
            return dict([(u,float(c)/maxscore) for(u,c) in scores.items()])

基于内容的排名的算法属于传统的早期搜索引擎使用的度量方式。其三种评价度量的代码分别如下:

###单词频度作为评价度量
    def frequencyscore(self,rows):
        count=dict([row[0],0] for row in rows)
        for row in rows:
            count[row[0]]+=1
        return self.normalizescores(count)

    ###文档位置
    def locationscore(self,rows):
        locations=dict([row[0],1000000] for row in rows)
        for row in rows:
            loc=sum(row[1:])
            if loc<locations[row[0]]:
                locations[row[0]]=loc

        return self.normalizescores(locations,smallIsBetter=1)
    #单词距离
    def distancescore(self,rows):
        ###如果仅有两个单词,则得分都一样
        if len(rows[0])<=2:
            return dict([(row[0],1.0) for row in rows ])
        ###初始化字典,并且填入一个很大的数字
        mindistance=dict([(row[0],1000000) for row in rows])

        for row in rows:
            dist=sum([abs(row[i]-row[i-1]) for i in range(2,len(row))])
            if dist<mindistance[row[0]]:
                mindistance[row[0]]=dist
        return self.normalizescores(mindistance,smallIsBetter=1)

2.著名的PageRank算法

Google使用的算法,为每一个网页都赋予了一个只是网页重要程度的评价值。网页的重要性是依指向该网页的所有的其他网页的重要性以及这些网页所包含的连接数求得的。

PageRank 使用一个阻尼因子:0.85

关于该算法的具体内容,可以自行百度。。。也可以查看http://www.cnblogs.com/FengYan/archive/2011/11/12/2246461.html的博文

代码实现:

 ####PageRank
    def calculatepagerank(self,iteration=20):
        ###清除当前的PageRank表
        self.con.execute("drop table if exists pagerank")
        self.con.execute("create table pagerank(urlid primary key,score)")
        ####初始化每一个url,令其PageRank值为1
        self.con.execute("insert into pagerank select rowid,1.0 from urllist")
        self.dbcommit()

        for i in range(iteration):
            print(‘Iteration %d‘ %(i))
            for(urlid,) in self.con.execute("select rowid from urllist"):
                pr=0.15 #(最小值)
                ##循环遍历指向当前网页的所有其他网页

                for(linker,) in self.con.execute("select distinct fromid from link where toid=%d" %urlid):
                    ###得到相应的PR值
                    linkingpr=self.con.execute("select score from pagerank where urlid=%d" %linker).fetchone()[0]

                    ###求得总的链接数
                    linkingcount=self.con.execute("select count(*) from link where fromid=%d " %linker).fetchone()[0]
                    pr+=0.85*(linkingpr/linkingcount)
                self.con.execute("update pagerank set score=%f where urlid=%d" %(pr,urlid))
            self.dbcommit()
时间: 2024-11-09 05:06:07

搜索与排名思路整理的相关文章

快速排序思路整理

引言: 快速排序和归并排序是面试当中常常被问到的两种排序算法,在研究过数据结构所有的排序算法后,个人认为最复杂的当属快速排序.从代码量上来看,快速排序并不多,我之所以认为快排难以掌握是因为快排是一种递归算法,同时终止条件较多.如果你刚刚把快排的思路整理过一遍可能觉得不难,然而一个月之后呢? 面试要求的是临场发挥,如果不是烂熟于心,基本就卡壳了.在面试官眼里,你和那些完全不懂快速排序算法的菜逼是一样的,也许实际上你可能私底下已经理解很多遍了,然而并没卵.所以当下问题就是,如何将快排烂熟于心?我觉得

16 飞机大战:思路整理、重要代码

思路整理 重要代码 0.重写方法万万检查记得要不要继承父类方法 def __init__(self): super().__init__() 1.创建游戏时钟:用来设置游戏刷新率 # 新建游戏时钟对象 self.clock = pygame.time.Clock() ... ... # 设置游戏刷新率 self.clock.tick(60) #60帧/s 2.精灵组 # 创建xx精灵 self.xx = Xx() #其中Xx是Xx类 # 创建xx精灵组 self.xx_group = pygam

能力库开发思路整理

能力库界面如下: 相关数据库表: 1 CREATE TABLE `base_ability` ( 2 `abillty_id` varchar(36) NOT NULL DEFAULT '' COMMENT '主键', 3 `ability_code` varchar(20) DEFAULT NULL, 4 `ability_name` varchar(20) DEFAULT NULL COMMENT '能力编号', 5 `ability_name_desc` varchar(255) DEFA

Canvas---Canvas图像处理、图片查看器实现思路整理、拖动边界控制

没想到一个图片查看器花了我这么多时间,而且没做好. 现在整理下思路,然后把不足的地方记一下,日后请教他人. 基本思路: 一.图片查看器功能---缩放 要实现自由缩放,先要实现图片对canvas的自适应,就是给你一张大图片,你能够把它合理缩放后恰好绘制在canvas中. 具体做法是:例如:图片为500*500,canvas为240*320,那就取缩放宽度为240,长度为240/500*500,利用缩放宽度与长度,绘制图片即可. 然后是自由缩放,这时,你的缩小放大对象只要是一个矩形就好,然后图片去适

Lync2013 升级Skype For Bussiness 2015 升级思路整理

最近做了次Lync 2013企业版升级到SFB 2015,期间碰到了各种问题.这里就专门整理下升级的思路. 至于升级过程实战的文章,后续有空再写写,其实还是很简单的. 后续todo:SFB 2015 后端alwaysOn建立,Lync 2013升级至SFB 2015并且后端进行AlwaysOn高可用建立(这个可能不靠谱--) 简要升级路线: Lync 2013:使用新的拓扑生成器生成新拓扑并发布,然后在池的每台关联服务器上就地升级功能升级 Lync 2010:首先升级至Lync 2013,然后使

《集体智慧编程》 第四章 搜索与排名 学习笔记

本章我们将学到如何检索网页.建立索引.对网页进行搜索,以及多种不同方式对搜索结果进行排名. 一.搜索引擎的组成 (一)建立搜索引擎的步骤: 1.找到一种搜索文档的方法. (1)有时可能会涉及针对网页的抓取:在互联网上先从一小组网页开始,然后再根据网页内的链接逐步追踪其它网页. (2)而有时可能需要我们在一组固定数量的文档范围内进行搜集,这些文档可能来自于某个公司的内部网络. 2.为文档建立索引 通常我们需要建立一张大的表,表中包含了文档及所有不同单词的位置信息. 3.通过查询返回一个经过排序的文

Lucene站内搜索的设计思路

正好近期部门有一个小需求需要做商品的搜索,虽然最终由于工作量等原因先做数据库搜索,我依然用刚接触的Lucene弄了一套自嗨. 首先看需求:搜索:根据商品标题和内容搜索 没错,就这么简单! 我想了想,数据源大概有以下两种: 1.根据需要搜索的字段,从数据库读取信息加载,并创建Lucene索引 2.利用爬虫定时抓取数据,并创建Lucene索引 由于是非正式的,数据库不方便访问,因此我选择利用爬虫去抓取指定网页,并清理内容来建立索引 其中,针对链接,做商品详情页的过滤,这样能保证最快速.最精准的建立我

Java做界面思路整理

说起大一就学过C++,但从未接触过VC++,至于做界面也是直到学java才开始,所以自己还是个新手啊... 步入正题,通过自己写的两个小程序,对做界面的思路进行一下整理. 首先,构想出自己想要实现的界面是什么样子.可以在纸上画出个轮廓(我是这么干的...),尽量详尽,比如点击按钮后的实现一个页面的跳转,跳转之后的页面也画出来.为什么要这样呢?都知道界面是由控件和容器组成的,画的目的就是清楚要用哪些组件,并且根据自己的界面,然后组织容器,再进而组织布局.对于布局可能会比较麻烦一点,这要根据你的窗口

leetCode 79.Word Search (词搜索) 解题思路和方法

The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once. For example, Given board = [ ["ABCE"]