搜索引擎原理模拟
基本原理#
搜索引擎由四部分组成:搜索器、索引器、检索器、用户接口。其运行过程如下:
- 通过爬虫抓取网页
- 建立索引数据库
- 按索引进行排序
- 处理用户请求,对查找结果排序
其中,网页的分析算法有pagerank,hits等,以下采用pagerank算法
搜索器#
爬取的页面#
为自己乱写了三个页面,便于验证自己的程序和之后的排序算法是否正确。这三个页面的引用关系为
爬虫#
采用scrapy框架
新建爬虫项目:scrapy startproject search
然后进入目录,新建爬虫: scrapy genspider MySearch 爬取的域名,之后可以修改
然后修改spiders/MySearch.py文件
其中start_url是开始爬取的链接,parse函数是爬取后对结果的处理
爬取后,将爬取的页面为以网页链接命名的文件保存在result文件夹中,如下
并且同时生成引用关系的文件result2/reltion.txt,用于保存各个页面间的引用关系,如下第一行为index.html引用了1.html
MySearch.py文件如下#
import scrapy
class MysearchSpider(scrapy.Spider):
name = 'MySearch'
start_urls = ['http://127.0.0.1/qilinweb/index.html']
url = []
def parse(self, response):
name1 = response.url.split('/')[-1]
f2 = open('D:/code/python/search/result/' + name1, 'w')
# 保存爬取的内容
f2.write(response.url + "\n" + response.text)
# 对页面进行分析,提取每一个链接,加入到url中
for href in response.css('a::attr(href)').extract():
if href not in self.url:
self.url.append(href)
try:
name2 = href
# 保存页面的引用关系
with open('D:/code/python/search/result2/relation.txt', 'a+') as f:
f.write(name2.split('/')[-1] + "," + name1 + "\n")
yield scrapy.Request(href, callback=self.parse) # 生成器,对每一个链接再进行爬取
except:
continue
然后scrapy crawl MySearch进行爬取
索引器#
因为页面比较简单,就不生成索引,直接对所有爬取保存的页面内容搜索
检索器#
分为检索和排序两部分
检索#
也比较简单,就是直接对所有爬取保存的页面内容搜索,代码在MyUser.py中的一小段
for file in fname:
f = open('D:/code/python/search/result/' + file, 'r')
if str in f.read():
result.append(file)
排序#
采用pagerank算法,因为该算法是静态的,所以在爬取完页面后就可以直接生成各页面的等级,页面等级以字典形式保存在result2/weight.txt中
代码为MyIndex.py中#
算法代码来自知乎https://zhuanlan.zhihu.com/p/81691075
但是感觉原作者算法实现好像有点问题,自己做了一下修改
# 用于存储图
class Graph():
def __init__(self):
self.linked_node_map = {} # 邻接表,
self.PR_map = {} # 存储每个节点的入度
self.L = {} # 存每个节点的出度
# 添加节点
def add_node(self, node_id):
if node_id not in self.linked_node_map:
self.linked_node_map[node_id] = set({})
self.PR_map[node_id] = 0
else:
print("这个节点已经存在")
# 增加一个从Node1指向node2的边。允许添加新节点
def add_link(self, node1, node2):
if node1 not in self.linked_node_map:
self.add_node(node1)
self.L[node1] = 0
else:
self.L[node1] = self.L[node1] + 1
if node2 not in self.linked_node_map:
self.add_node(node2)
self.L[node2] = 0
else:
self.L[node2] = self.L[node2] + 1
self.linked_node_map[node1].add(node2) # 为node1添加一个邻接节点,表示ndoe2引用了node1
# 计算pr
def get_PR(self, epoch_num=3, d=0.5): # 配置迭代轮数,以及阻尼系数
for i in range(epoch_num):
for node in self.PR_map: # 遍历每一个节点
self.PR_map[node] = (1 - d) + d * sum(
[self.PR_map[temp_node] / self.L[temp_node] for temp_node in self.linked_node_map[node]]) # 原始版公式
with open('D:/code/python/search/result2/weight.txt', 'w') as f:
f.write(str(self.PR_map))
graph = Graph()
with open('D:/code/python/search/result2/relation.txt', 'r') as f:
for line in f:
node1, node2 = line.strip().split(',')
graph.add_link(node1, node2)
graph.get_PR()
用户接口#
对输入的内容进行搜索,并且按页面的等级从高到低显示
程序在MyUser.py中
import os
import ast
# 按照页面等级排序
def p(s):
return str[s]
str = input("搜索:")
fname = os.listdir(r'D:\code\python\search\result')
result = []
for file in fname:
f = open('D:/code/python/search/result/' + file, 'r')
if str in f.read():
result.append(file)
f2 = open('D:/code/python/search/result2/weight.txt', 'r')
str = f2.read()
str = ast.literal_eval(str) # 将读入的字符串转化为字典类型
show = sorted(result, key=p, reverse=True)
print("搜索结果如下\n")
print(show)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性