用python写一个简单的中文搜索引擎
来源:互联网 发布:java session工作原理 编辑:程序博客网 时间:2024/05/21 06:01
搜索引擎可以用Nutch等工具来配置,也可以自己写代码实现,作为一个小练习。
要做的搜索引擎搜索范围限定在某个新闻网站内部,和百度site:(指定网址)的功能类似。把爬虫和解析的代码改改,也可以用于其他的搜索场合。
使用的编程语言为python。
这篇文章把搜索引擎需要的步骤都讲的很清楚了:
http://073palmer.blogspot.com/2012/06/python.html
打不开的朋友可以看这个:
http://blog.sina.com.cn/s/blog_6f97247e0102vuyo.html
(转载来转载去,也不知道到底哪个才是原作者链接)
不过这篇文章里的搜索引擎是基于英文单词的,而我写的主要是中文文本。
首先放个github链接。
https://github.com/RyinSummers/AVerySimpleSearchEngine
总的来说,要达成目标,一共有如下几个步骤:
- 爬取这个网站,得到所有网页链接。
- 得到网页的源代码,解析剥离出想要的内容。
- 把内容做成词条索引,保存起来。我使用的是最简陋的倒排表。
- 搜索时,根据搜索词在词条索引里查询,按顺序返回相关的搜索结果。我使用的评价方式是最简陋的tfidf,而且tfidf值并非在建表时保存,而是在每次查询时再计算。
步骤3和4使用的方式性能有待提升,不过,因为要爬的这个网站网页数量不过万,规模较小,所以效率目前没什么影响。
开始之前,需要几点预备知识。
1.网页爬虫,python代码如下。
from urllib import requestresponse=request.urlopen('http://www.baidu.com')content=response.read().decode('gb18030')#print(content)#将会print整个网页的html源码
2.爬虫使用BFS算法,用到的数据结构为队列deque和集合set。队列存储当前准备访问的url,集合存储所有走过的url。队列使用的python代码如下。
from collections import dequequeue=deque(['队列元素1','队列元素2','队列元素3'])queue.append('队列元素4')queue.popleft()#队首出队print(queue)
3.正则表达式,可以检测一个字符串是不是符合某个格式,或者把一个字符串里特定格式的部分提取出来。用于匹配html代码中的特定内容。廖雪峰网站的Python教程有介绍正则表达式,讲的很清楚:
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143193331387014ccd1040c814dee8b2164bb4f064cff000
比如,
\d匹配一个数字,
\w匹配一个字母或数字,
\s匹配一个空白符号,
.匹配任意一个字符,
\加上一个特殊符号作为转义,
*匹配零或多个字符,
+表示至少一个字符,
?表示0个或1个字符,
{n}表示n个字符,
{n,m}表示n~m个字符,
中文[\u4e00-\u9fa5]+
python代码如下。
import re#re.match返回一个Match对象if re.match(r'href=\".*view\.sdu\.edu\.cn.*\"','href="http://www.view.sdu.edu.cn/new/"'): print('ok')else: print('failed')
4.BeautifulSoup,python的一个超超超好用的html解析工具包,需要额外下载安装。官方文档介绍的很清楚了。
http://beautifulsoup.readthedocs.io/zh_CN/latest/
5.用到sqlite,是python和数据库的基础知识。python代码如下。
import sqlite3conn=sqlite3.connect('databasetest.db')c=conn.cursor()#创建一个表c.execute('create table doc (id int primary key,link text)')#往表格插入一行数据num=1link='www.baidu.com'c.execute('insert into doc values (?,?)',(num,link))#查询表格内容c.execute('select * from doc')#得到查询结果result=c.fetchall()print(type(result),result)conn.commit()conn.close()
6.jieba分词,一个中文分词工具,需要额外下载安装。
有三种分词模式,运行print一下就能感受到异同。python代码如下。
import jieba#精确模式,试图将句子最精确地切开,适合文本分析seglist=jieba.cut('阴阳师总是抽不到茨木童子好伤心')print('/'.join(seglist))#全模式,把句子中所有的可以成词的词语都扫描出来,速度非常快,但是不能解决歧义seglist=jieba.cut('阴阳师总是抽不到茨木童子好伤心',cut_all=True)print('/'.join(seglist))#搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词seglist=jieba.cut_for_search('阴阳师总是抽不到茨木童子好伤心')print('/'.join(seglist))
有了预备知识,就正式可以开始写代码了。
前面说过的,四个步骤中的步骤1-3,存储在search_engine_build.py。
爬取的新闻网站为view.edu.sdu.cn。
建立两个table,一个是doc表,存储网页链接。
一个是word表,即为倒排表,存储词语和其对应的doc序号的list。如果一个词在某个网页里出现多次,那么list里这个网页的序号也出现多次。list转换成一个字符串存进数据库。比如,某个词出现在1、2、3号doc里,它的list应为[1,2,3],转换成字符串”1 2 3“存储在数据库。
import sysfrom collections import dequeimport urllibfrom urllib import requestimport refrom bs4 import BeautifulSoupimport lxmlimport sqlite3import jiebasafelock=input('你确定要重新构建约5000篇文档的词库吗?(y/n)')if safelock!='y': sys.exit('终止。')url='http://www.view.sdu.edu.cn'#入口queue=deque()#待爬取链接的集合,使用广度优先搜索visited=set()#已访问的链接集合queue.append(url)conn=sqlite3.connect('viewsdu.db')c=conn.cursor()#在create table之前先drop table是因为我之前测试的时候已经建过table了,所以再次运行代码的时候得把旧的table删了重新建c.execute('drop table doc')c.execute('create table doc (id int primary key,link text)')c.execute('drop table word')c.execute('create table word (term varchar(25) primary key,list text)')conn.commit()conn.close()print('***************开始!***************************************************')cnt=0while queue: url=queue.popleft() visited.add(url) cnt+=1 print('开始抓取第',cnt,'个链接:',url) #爬取网页内容 try: response=request.urlopen(url) content=response.read().decode('gb18030') except: continue #寻找下一个可爬的链接,因为搜索范围是网站内,所以对链接有格式要求,这个格式要求根据具体情况而定 m=re.findall(r'<a href=\"([0-9a-zA-Z\_\/\.\%\?\=\-\&]+)\" target=\"_blank\">',content,re.I) for x in m: if re.match(r'http.+',x): if not re.match(r'http\:\/\/www\.view\.sdu\.edu\.cn\/.+',x): continue elif re.match(r'\/new\/.+',x): x='http://www.view.sdu.edu.cn'+x else: x='http://www.view.sdu.edu.cn/new/'+x if (x not in visited) and (x not in queue): queue.append(x) #解析网页内容,可能有几种情况,这个也是根据这个网站网页的具体情况写的 soup=BeautifulSoup(content,'lxml') title=soup.title article=soup.find('div',class_='text_s',id='content') author=soup.find('div',class_='text_c') if title==None and article==None and author==None: print('无内容的页面。') continue elif article==None and author==None: print('只有标题。') title=title.text title=''.join(title.split()) article='' author='' # elif title==None and author==None: # print('只有内容。') # title='' # article=article.get_text("",strip=True) # article=' '.join(article.split()) # author='' # elif title==None and article==None: # print('只有作者。') # title='' # article='' # author=author.find_next_sibling('div',class_='text_c').get_text("",strip=True) # author=' '.join(author.split()) # elif title==None: # print('有内容有作者,缺失标题') # title='' # article=article.get_text("",strip=True) # article=' '.join(article.split()) # author=author.find_next_sibling('div',class_='text_c').get_text("",strip=True) # author=' '.join(author.split()) elif article==None: print('有标题有作者,缺失内容')#视频新闻 title=soup.h1.text title=''.join(title.split()) article='' author=author.get_text("",strip=True) author=''.join(author.split()) elif author==None: print('有标题有内容,缺失作者') title=soup.h1.text title=''.join(title.split()) article=article.get_text("",strip=True) article=''.join(article.split()) author='' else: title=soup.h1.text title=''.join(title.split()) article=article.get_text("",strip=True) article=''.join(article.split()) author=author.find_next_sibling('div',class_='text_c').get_text("",strip=True) author=''.join(author.split()) print('网页标题:',title) #提取出的网页内容存在title,article,author三个字符串里,对它们进行中文分词 seggen=jieba.cut_for_search(title) seglist=list(seggen) seggen=jieba.cut_for_search(article) seglist+=list(seggen) seggen=jieba.cut_for_search(author) seglist+=list(seggen) #数据存储 conn=sqlite3.connect("viewsdu.db") c=conn.cursor() c.execute('insert into doc values(?,?)',(cnt,url)) #对每个分出的词语建立词表 for word in seglist: #print(word) #检验看看这个词语是否已存在于数据库 c.execute('select list from word where term=?',(word,)) result=c.fetchall() #如果不存在 if len(result)==0: docliststr=str(cnt) c.execute('insert into word values(?,?)',(word,docliststr)) #如果已存在 else: docliststr=result[0][0]#得到字符串 docliststr+=' '+str(cnt) c.execute('update word set list=? where term=?',(docliststr,word)) conn.commit() conn.close() print('词表建立完毕=======================================================')
以上的代码只需运行一次即可,搜索引擎所需的数据库已经建好了。
需要搜索的时候,执行search_engine_use.py,此为步骤4。代码如下。
import reimport urllibfrom urllib import requestfrom collections import dequefrom bs4 import BeautifulSoupimport lxmlimport sqlite3import jiebaimport mathconn=sqlite3.connect("viewsdu.db")c=conn.cursor()c.execute('select count(*) from doc')N=1+c.fetchall()[0][0]#文档总数target=input('请输入搜索词:')seggen=jieba.cut_for_search(target)score={}#文档号:文档得分for word in seggen: print('得到查询词:',word) #计算score tf={}#文档号:文档数 c.execute('select list from word where term=?',(word,)) result=c.fetchall() if len(result)>0: doclist=result[0][0] doclist=doclist.split(' ') doclist=[int(x) for x in doclist]#把字符串转换为元素为int的list df=len(set(doclist))#当前word对应的df数 idf=math.log(N/df) print('idf:',idf) for num in doclist: if num in tf: tf[num]=tf[num]+1 else: tf[num]=1 #tf统计结束,现在开始计算score for num in tf: if num in score: #如果该num文档已经有分数了,则累加 score[num]=score[num]+tf[num]*idf else: score[num]=tf[num]*idfsortedlist=sorted(score.items(),key=lambda d:d[1],reverse=True)#print('得分列表',sortedlist)cnt=0for num,docscore in sortedlist: cnt=cnt+1 c.execute('select link from doc where id=?',(num,)) url=c.fetchall()[0][0] print(url,'得分:',docscore) try: response=request.urlopen(url) content=response.read().decode('gb18030') except: print('oops...读取网页出错') continue soup=BeautifulSoup(content,'lxml') title=soup.title if title==None: print('No title.') else: title=title.text print(title) if cnt>20: breakif cnt==0: print('无搜索结果')
- 用python写一个简单的中文搜索引擎
- 用python写一个简单的天气预报
- 用Python写一个简单的爬虫
- 用python写的一个简单的记事本
- 用python写的一个简单的计算器
- 用Python写的一个简单的小说下载器
- 用python的cmd模块写一个简单的shell
- python写的一个简单的spider
- python写的一个简单的英汉翻译
- 用Python写一个简单的中文分词器
- 【转载】用Python写一个简单的中文分词器
- 用python写一个简单的爬虫功能
- 用python写一个简单的推荐系统
- 用python写一个简单的推荐系统
- 用Python写一个简单的爬虫功能
- 用python写一个简单的爬虫功能
- 用Python写一个简单的微博爬虫
- 用python写一个简单的倒计时软件
- cb中写的代码
- 初识Android Studio的项目结构
- 欢迎使用CSDN-markdown编辑器
- 好消息:谷歌开发者中文站上线啦
- Mac使用技巧——快捷键
- 用python写一个简单的中文搜索引擎
- 问答机器人短期规划
- UEditor chrome 点击上传文件选择框会延迟几秒才会显示
- Jquery-UI—制作可折叠面板(accordion)
- AndroidStudio导入项目作为library
- 各种web项目的实现及源码归纳总结
- 使用HttpClient和Jsoup爬取某网的妹子图片
- 用原始的方式学习编程
- 快速排序法