python搜索引擎(转)
用python如何实现一个站内搜索引擎?
先想想搜索引擎的工作流程:
1、网页搜集。用深度或者广度优先的方法搜索某个网站,保存下所有的网页,对于网页的维护采用定期搜集和增量搜集的方式。
2、建立索引库。首先,过滤掉重复的网页,虽然他们有不同的URL;然后,提取出网页的正文;最后,对正文切词,建立索引。索引总要有个顺序,利用pagerank算法给每个网页加个权值。
3、提供搜索服务。首先,切分查询词;然后,对索引结果排序,结合原来的权值和用户的查询历史等作为新的索引顺序;最后,还要显示文档摘要。
完整流程如下:
-----------------------------------以下文字引用自 万维网Web自动搜索引擎(技术报告)邓雄(Johnny Deng) 2006.12
“网络蜘蛛”从互联网上抓取网页,把网页送入“网页数据库”,从网页中“提取URL”,把URL送入“URL数据库”,“蜘蛛控制”得到网页的URL,控制“网络蜘蛛”抓取其它网页,反复循环直到把所有的网页抓取完成。
系统从“网页数据库”中得到文本信息,送入“文本索引”模块建立索引,形成“索引数据库”。同时进行“链接信息提取”,把链接信息(包括锚文本、链接本身等信息)送入“链接数据库”,为“网页评级”提供依据。
“用户”通过提交查询请求给“查询服务器”,服务器在“索引数据库”中进行相关网页的查找,同时“网页评级”把查询请求和链接信息结合起来对搜索结果进行相关度的评价,通过“查询服务器”按照相关度进行排序,并提取关键词的内容摘要,组织最后的页面返回给“用户”。
-----------------------------------引用到此结束
写一个搜索引擎的想法源自于我正在学习python语言,想借此来驱动自己。
目前有思路的三个模块:网页爬虫(广度优先搜索),提取网页正文(cx-extractor),中文分词(smallseg)。
网页爬虫
广度优先搜索,抓取新浪站内10000个页面(url中含有‘sina.com.cn/’的页面)
抓取: urllib2.urlopen()
解析:htmllib.HTMLParser
存储:redis
每个URL对应一个IDSEQ序列(从1000000开始增加)
URL:IDSEQ 存储URL
PAGE:IDSEQ 存储URL对应的HTML页面源码
URLSET:IDSEQ 每个URL对应一个指向它的URL(IDSEQ)集合
代码如下:
#!/usr/bin/python
from spdUtility import PriorityQueue,Parser
import urllib2
import sys
import os
import inspect
import time
g_url = 'http://www.sina.com.cn'
g_key = 'www'
"""
def line():
try:
raise Exception
except:
return sys.exc_info()[].tb_frame.f_back.f_lineno""" def updatePriQueue(priQueue, url):
extraPrior = url.endswith('.html') and or
extraMyBlog = g_key in url and or
item = priQueue.getitem(url)
if item:
newitem = (item[]++extraPrior+extraMyBlog, item[])
priQueue.remove(item)
priQueue.push(newitem)
else :
priQueue.push( (+extraPrior+extraMyBlog,url) ) def getmainurl(url):
ix = url.find('/',len('http://') )
if ix > :
return url[:ix]
else :
return url
def analyseHtml(url, html, priQueue, downlist):
p = Parser()
try :
p.feed(html)
p.close()
except:
return
mainurl = getmainurl(url)
print mainurl
for (k, v) in p.anchors.items():
for u in v :
if not u.startswith('http://'):
u = mainurl + u
if not downlist.count(u):
updatePriQueue( priQueue, u) def downloadUrl(id, url, priQueue, downlist,downFolder):
downFileName = downFolder+'/%d.html' % (id,)
print 'downloading', url, 'as', downFileName, time.ctime(),
try:
fp = urllib2.urlopen(url)
except:
print '[ failed ]'
return False
else :
print '[ success ]'
downlist.push( url )
op = open(downFileName, "wb")
html = fp.read()
op.write( html )
op.close()
fp.close()
analyseHtml(url, html, priQueue, downlist)
return True def spider(beginurl, pages, downFolder):
priQueue = PriorityQueue()
downlist = PriorityQueue()
priQueue.push( (,beginurl) )
i =
while not priQueue.empty() and i < pages :
k, url = priQueue.pop()
if downloadUrl(i+, url, priQueue , downlist, downFolder):
i +=
print '\nDownload',i,'pages, Totally.' def main():
beginurl = g_url
pages =
downloadFolder = './spiderDown'
if not os.path.isdir(downloadFolder):
os.mkdir(downloadFolder)
spider( beginurl, pages, downloadFolder) if __name__ == '__main__':
main()
后期优化:
目前程序抓取速度较慢,瓶颈主要在urllib2.urlopen等待网页返回,此处可提出来单独做一个模块,改成多进程实现,redis的list可用作此时的消息队列。
扩展程序,实现增量定期更新。
抽取网页正文
根据提陈鑫的论文《基于行块分布函数的通用网页正文抽取算法》,googlecode上的开源项目(http://code.google.com/p/cx-extractor/)
“作者将网页正文抽取问题转化为求页面的行块分布函数,这种方法不用建立Dom树,不被病态HTML所累(事实上与HTML标签完全无关)。通过在线性时间内建立的行块分布函数图,直接准确定位网页正文。同时采用了统计与规则相结合的方法来处理通用性问题。作者相信简单的事情总应该用最简单的办法来解决这一亘古不变的道理。整个算法实现代码不足百行。但量不在多,在法。”
他的项目中并没有python版本的程序,下面的我根据他的论文和其他代码写的python程序,短小精悍,全文不过50行代码:
#!/usr/bin/python
#coding=utf-
#根据 陈鑫《基于行块分布函数的通用网页正文抽取算法》
#Usage: ./getcontent.py filename.html
import re
import sys
def PreProcess():
global g_HTML
_doctype = re.compile(r'<!DOCTYPE.*?>', re.I|re.S)
_comment = re.compile(r'<!--.*?-->', re.S)
_javascript = re.compile(r'<script.*?>.*?<\/script>', re.I|re.S)
_css = re.compile(r'<style.*?>.*?<\/style>', re.I|re.S)
_other_tag = re.compile(r'<.*?>', re.S)
_special_char = re.compile(r'&.{1,5};|&#.{1,5};')
g_HTML = _doctype.sub('', g_HTML)
g_HTML = _comment.sub('', g_HTML)
g_HTML = _javascript.sub('', g_HTML)
g_HTML = _css.sub('', g_HTML)
g_HTML = _other_tag.sub('', g_HTML)
g_HTML = _special_char.sub('', g_HTML)
def GetContent(threshold):
global g_HTMLBlock
nMaxSize = len(g_HTMLBlock)
nBegin =
nEnd =
for i in range(, nMaxSize):
if g_HTMLBlock[i]>threshold and i+<nMaxSize and g_HTMLBlock[i+]> and g_HTMLBlock[i+]> and g_HTMLBlock[i+]>:
nBegin = i
break
else:
return None
for i in range(nBegin+, nMaxSize):
if g_HTMLBlock[i]== and i+<nMaxSize and g_HTMLBlock[i+]==:
nEnd = i
break
else:
return None
return '\n'.join(g_HTMLLine[nBegin:nEnd+])
if __name__ == '__main__' and len(sys.argv) > :
f = file(sys.argv[], 'r')
global g_HTML
global g_HTMLLine
global g_HTMLBlock
g_HTML = f.read()
PreProcess()
g_HTMLLine = [i.strip() for i in g_HTML.splitlines()] #先分割成行list,再过滤掉每行前后的空字符
HTMLLength = [len(i) for i in g_HTMLLine] #计算每行的长度
g_HTMLBlock = [HTMLLength[i] + HTMLLength[i+] + HTMLLength[i+] for i in range(, len(g_HTMLLine)-)] #计算每块的长度
print GetContent()
上面是一个demo程序,真正用使用起来需要增加存储功能。
依然是采用redis存储,读出所有的page页(keys 'PAGE:*'),提取正文,判断正文是否已在容器中(排除URL不同的重复页面),如果在容器中则做下一次循环,不在容器中则加入容器并存储到 CONTENT:IDSEQ 中。
代码如下:
#!/usr/bin/python
#coding=utf-
#根据 陈鑫《基于行块分布函数的通用网页正文抽取算法》
import re
import sys
import redis
import bisect
def PreProcess():
global g_HTML
_doctype = re.compile(r'<!DOCTYPE.*?>', re.I|re.S)
_comment = re.compile(r'<!--.*?-->', re.S)
_javascript = re.compile(r'<script.*?>.*?<\/script>', re.I|re.S)
_css = re.compile(r'<style.*?>.*?<\/style>', re.I|re.S)
_other_tag = re.compile(r'<.*?>', re.S)
_special_char = re.compile(r'&.{1,5};|&#.{1,5};')
g_HTML = _doctype.sub('', g_HTML)
g_HTML = _comment.sub('', g_HTML)
g_HTML = _javascript.sub('', g_HTML)
g_HTML = _css.sub('', g_HTML)
g_HTML = _other_tag.sub('', g_HTML)
g_HTML = _special_char.sub('', g_HTML)
def GetContent(threshold):
global g_HTMLBlock
nMaxSize = len(g_HTMLBlock)
nBegin =
nEnd =
for i in range(, nMaxSize):
if g_HTMLBlock[i]>threshold and i+<nMaxSize and g_HTMLBlock[i+]> and g_HTMLBlock[i+]> and g_HTMLBlock[i+]>:
nBegin = i
break
else:
return None
for i in range(nBegin+, nMaxSize):
if g_HTMLBlock[i]== and i+<nMaxSize and g_HTMLBlock[i+]==:
nEnd = i
break
else:
return None
return '\n'.join(g_HTMLLine[nBegin:nEnd+])
def BinarySearch(UniqueSet, item):
if len(UniqueSet) == :
return
left =
right = len(UniqueSet)-
mid = -
while left <= right:
mid = (left+right)/
if UniqueSet[mid] < item :
left = mid +
elif UniqueSet[mid] > item :
right = mid -
else:
break
return UniqueSet[mid] == item and or
if __name__ == '__main__':
global g_redisconn
global g_HTML
global g_HTMLLine
global g_HTMLBlock
g_redisconn = redis.Redis()
UniqueSet = []
keys = g_redisconn.keys('PAGE:*')
nI =
for key in keys:
g_HTML = g_redisconn.get(key)
PreProcess()
g_HTMLLine = [i.strip() for i in g_HTML.splitlines()] #先分割成行list,再过滤掉每行前后的空字符
HTMLLength = [len(i) for i in g_HTMLLine] #计算每行的长度
g_HTMLBlock = [HTMLLength[i] + HTMLLength[i+] + HTMLLength[i+] for i in range(, len(g_HTMLLine)-)] #计算每块的长度
sContent = GetContent()
if sContent != None:
sContentKey = key.replace('PAGE', 'CONTENT')
if BinarySearch(UniqueSet, sContent) == :
bisect.insort(UniqueSet, sContent)
g_redisconn.set(sContentKey, sContent)
中文分词
smallseg -- 开源的,基于DFA的轻量级的中文分词工具包
特点:可自定义词典、切割后返回登录词列表和未登录词列表、有一定的新词识别能力。
下载地址:http://code.google.com/p/smallseg/downloads/detail?name=smallseg_0.6.tar.gz&can=2&q
总结
python简洁易用,类库齐全,但在国内应用还不是十分广泛,各类中文资源不是很多,尤其是深入讲解文档很少。
到目前为止我已经简单实现了搜索引擎的几个部分,可以说是十分粗糙,没有写这个搜索的客户端,因为到目前为止已经发现了足够多的问题待解决和优化。
优化
爬虫部分:采用pycurl替换urllib2,更快速,支持user-agent配置;增加字符集识别,统一转换成一种字符后再存储;多进程优化。
url去重复:取url的fingerprint,用redis存储
page去重:取页面的fingerprint,用redis存储,比较相似性
如果有更大的数据量,就要考虑采用分布式存储;如果有更精准的要求,就要考虑一个新的分词系统。
转自:http://www.cnblogs.com/favourmeng/archive/2012/09/20/2695514.html
python搜索引擎(转)的更多相关文章
- python 搜索引擎Whoosh中文文档和代码 以及jieba的使用
注意, 数据库的表最好别有下划线 中文文档链接: https://mr-zhao.gitbooks.io/whoosh/content/%E5%A6%82%E4%BD%95%E7%B4%A2%E5%B ...
- Python 资源大全中文版
Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-python 是 vinta 发起维护的 Python 资源列 ...
- [转载]Python 资源大全
原文链接:Python 资源大全 环境管理 管理 Python 版本和环境的工具 p – 非常简单的交互式 python 版本管理工具. pyenv – 简单的 Python 版本管理工具. Vex ...
- python常用库
本文由 伯乐在线 - 艾凌风 翻译,Namco 校稿.未经许可,禁止转载!英文出处:vinta.欢迎加入翻译组. Awesome Python ,这又是一个 Awesome XXX 系列的资源整理,由 ...
- python 第三方模块 转 https://github.com/masterpy/zwpy_lst
Chardet,字符编码探测器,可以自动检测文本.网页.xml的编码. colorama,主要用来给文本添加各种颜色,并且非常简单易用. Prettytable,主要用于在终端或浏览器端构建格式化的输 ...
- Python 库大全
作者:Lingfeng Ai链接:http://www.zhihu.com/question/24590883/answer/92420471来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非 ...
- Python资源汇总
Python 目录: 管理面板 算法和设计模式 反垃圾邮件 资产管理 音频 验证 构建工具 缓存 ChatOps工具 CMS 代码分析和Linter 命令行工具 兼容性 计算机视觉 并发和并行性 组态 ...
- Python常用库大全
环境管理 管理 Python 版本和环境的工具 p – 非常简单的交互式 python 版本管理工具. pyenv – 简单的 Python 版本管理工具. Vex – 可以在虚拟环境中执行命令. v ...
- [转]Python 资源大全中文版
摘自:https://github.com/jobbole/awesome-python-cn 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesom ...
随机推荐
- 使用Apache Curator监控Zookeeper的Node和Path的状态
1.Zookeeper经常被我们用来做配置管理,配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同 ...
- java与C#的基础语法区别--持续更新
1.判断字符串是否相等 java : equals()比较的是对象的内容(区分字母的大小写格式),但是如果使用“==”比较两个对象时,比较的是两个对象的内存地址,所以不相等.即使它们内容相等,但是不同 ...
- Item 6 消除过期的对象引用
过期对象引用没有清理掉,会导致内存泄漏.对于没有用到的对象引用,可以置空,这是一种做法.而最好的做法是,把保存对象引用的变量清理掉,多用局部变量. 什么是内存泄漏? 在Java中,对象的内存空间回 ...
- [LA3523/uva10195]圆桌骑士 tarjan点双连通分量+奇环定理+二分图判定
1.一个环上的各点必定在同一个点双连通分量内: 2.如果一个点双连通分量是二分图,就不可能有奇环: 最基本的二分图中的一个环: #include<cstdio> #include<c ...
- Machine Learning(CF940F+带修改莫队)
题目链接:http://codeforces.com/problemset/problem/940/F 题目: 题意:求次数的mex,mex的含义为某个集合(如{1,2,4,5})第一个为出现的非负数 ...
- 15、简述MySQL的执行计划?
具体的Mysql的执行计划,请参考下面的链接: MySQL_执行计划详细说明
- lnmp、lamp、lnmpa一键安装包(Updated: 2016-4-12)
lnmp.lamp.lnmpa一键安装包(Updated: 2016-4-12) 文章目录 脚本特性 安装步骤 如何添加虚拟主机? 如何删除虚拟主机? 如何管理ftp账号? 数据备份 如何管理服务 ...
- python之计算器
开发一个简单的python计算器 1.实现加减乘除及拓号优先级解析 2.用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * ...
- 网络知识===wireshark抓包数据分析(一)
wireshark分析: 上图是我进行一个HTTP协议的下载,文件内容大概是1.7M左右. 抓包数据: https://files.cnblogs.com/files/botoo/wireshark% ...
- ASPxgridview 编辑列初始化事件
在初始化编辑咧的时候,给其赋值或者是disable等等.... 贴上代码 protected void master_CellEditorInitialize(object sender, ASPxG ...