我把代码和爬好的数据放在了git上,欢迎大家来参考

https://github.com/linyi0604/linyiSearcher

我是在 manjaro linux下做的, 使用python3 语言, 爬虫部分涉及到 安装ChromeDriver 可以参考我之前写的博文。

建立索引部分参考: https://baijiahao.baidu.com/s?id=1597426056496128414&wfr=spider&for=pc

检索过程,衡量文档相似度使用了余弦相似度,参考:https://www.cnblogs.com/liangjf/p/8283519.html

  1. 为了完成我的信息检索选修课大作业,写下了这个简单的小项目。
  2.  
  3. 这里是一个python3 实现的简易的搜索引擎
  4.  
  5. 我把它取名叫linyiSearcher
  6.  
  7. --------
  8.  
  9. 所需要的python依赖包在requirements.txt
    可以使用 pip install -r requirements.txt 一次性安装全部
  10.  
  11. --------
  12.  
  13. 一共分成3部分完成(后面有稍微详细点的解读)
  14.  
  15. 1_spider.py 是一个爬虫, 爬取搜索引擎的语料库
  16.  
  17. 2_clean_data_and_make_index 是对爬下来的数据 进行一些清晰工作,并且将数据存入数据库,建立索引
  18.  
  19. 这里使用了 sqlite数据库,为了方便数据和项目一同携带
  20.  
  21. 3_searcher.py 简易的web后端, 实现了
  22.  
  23. 1 在网页输入搜索关键字, 在后端接收到关键字
  24.  
  25. 2 对关键字进行分词
  26.  
  27. 3 在索引中查找和关键字有关的文档
  28.  
  29. 4 按照余弦相似度 对文档进行排序
  30.  
  31. 5 把相近的文档展示出来
  32.  
  33. --------
  34.  
  35. 自己的知识储备和代码能力都捉襟见肘。
  36.  
  37. 大神来看,还望海涵~欢迎大家批评指正共同学习
  38.  
  39. --------
  40.  
  41. 1 爬虫:
  42.  
  43. 因为没有数据,只能写爬虫来做, 又只有自己的笔记本来跑,所以数据量也做不到非常大
  44.  
  45. 在这里 写了1程序 爬了百度贴吧 娱乐明星分类下面的所有1级页面帖子的标题 当做语料库
  46.  
  47. 爬取下来的数据存在了 ./data/database.csv
  48.  
  49. 数据有2 分别是 title url
  50.  
  51. 2 数据清洗 建立索引:
  52.  
  53. database.db 是一个sqlite数据库文件
  54.  
  55. 首先将每个文档存到了数据库当中
  56.  
  57. 数据库表为 page_info(id,keyword, title, url)
  58.  
  59. id 自增主键
  60.  
  61. keyword: 存了该文档文字用jieba分词打散后的词汇列表(用空格隔开所有词语的字符串)
  62.  
  63. title: 文档的文字内容
  64.  
  65. url: 该文档的网页链接
  66.  
  67. 然后 把每个文档 使用jieba分词工具, 打散成词语,把所有词语放到一个集合中(集合能去重)
  68.  
  69. 把所有词 存入数据库 建立索引
  70.  
  71. 索引这样理解:
  72.  
  73. 关键词: 你好 包含关键词的文档: <1,2,6,8,9>
  74.  
  75. 表为 page_index(id, word, page_id)
  76.  
  77. id: 自增 主键
  78.  
  79. word: 当前关键词
  80.  
  81. page_id: 包含该关键词的文档id 也就是page_info.id
  82.  
  83. 3 实现检索:
  84.  
  85. 首先 使用了bottle框架,是一个非常轻巧的web后端框架,实现了一个简单的web后端
  86.  
  87. 前端页面使用了bootstrap css样式,,毕竟自己什么垃圾的一p
  88.  
  89. 检索的实现过程:
  90.  
  91. 1 后端拿到检索的关键词,用jieba分词 把拿到的语句打散成词汇 形成关键词keyword_list
  92.  
  93. 2 在建立的索引表page_index中,搜关keyword_list中出现的词汇的page_id
  94.  
  95. 3 在包含所有keyword的文档上 计算和keyword的余弦相似度,然后降序排列
  96.  
  97. 4 返回给前端显示搜索结果
  98.  
  99. 看看检索结果:

  1.  

  1.  
  1.  
  2. 1_spider.py 爬虫的代码
  1. import requests
  2. from lxml import etree
  3. import random
  4. import COMMON
  5. import os
  6. from selenium import webdriver
  7. import pandas as pd
  8. """
  9. 这里是建立搜索引擎的第一步
  10. """
  11.  
  12. class Spider_BaiduTieba(object):
  13.  
  14. def __init__(self):
  15. self.start_url = "/f/index/forumpark?pcn=娱乐明星&pci=0&ct=1&rn=20&pn=1"
  16. self.base_url = "http://tieba.baidu.com"
  17. self.headers = COMMON.HEADERS
  18. self.driver = webdriver.Chrome()
  19. self.urlset = set()
  20. self.titleset = set()
  21.  
  22. def get(self, url):
  23. header = random.choice(self.headers)
  24. response = requests.get(url=url, headers=header, timeout=10)
  25. return response.content
  26.  
  27. def parse_url(self, url):
  28. """通过url 拿到xpath对象"""
  29. print(url)
  30. header = random.choice(self.headers)
  31. response = requests.get(url=url, headers=header, timeout=10)
  32. # 如果获取的状态码不是200 则抛出异常
  33. assert response.status_code == 200
  34. xhtml = etree.HTML(response.content)
  35. return xhtml
  36.  
  37. def get_base_url_list(self):
  38. """获得第一层url列表"""
  39. if os.path.exists(COMMON.BASE_URL_LIST_FILE):
  40. li = self.read_base_url_list()
  41. return li
  42. next_page = [self.start_url]
  43. url_list = []
  44. while next_page:
  45. next_page = next_page[0]
  46. xhtml = self.parse_url(self.base_url + next_page)
  47. tmp_list = xhtml.xpath('//div[@id="ba_list"]/div/a/@href')
  48. url_list += tmp_list
  49. next_page = xhtml.xpath('//div[@class="pagination"]/a[@class="next"]/@href')
  50. print(next_page)
  51. self.save_base_url_list(url_list)
  52. return url_list
  53.  
  54. def save_base_url_list(self, base_url_list):
  55. with open(COMMON.BASE_URL_LIST_FILE, "w") as f:
  56. for u in base_url_list:
  57. f.write(self.base_url + u + "\n")
  58.  
  59. def read_base_url_list(self):
  60. with open(COMMON.BASE_URL_LIST_FILE, "r") as f:
  61. line = f.readlines()
  62. li = [s.strip() for s in line]
  63. return li
  64.  
  65. def driver_get(self, url):
  66. try:
  67. self.driver.set_script_timeout(5)
  68. self.driver.get(url)
  69. except:
  70. self.driver_get(url)
  71. def run(self):
  72. """爬虫程序入口"""
  73. # 爬取根网页地址
  74. base_url_list = self.get_base_url_list()
  75. data_list = []
  76. for url in base_url_list:
  77. self.driver_get(url)
  78. html = self.driver.page_source
  79. xhtml = etree.HTML(html)
  80. a_list = xhtml.xpath('//ul[@id="thread_list"]//a[@rel="noreferrer"]')
  81. for a in a_list:
  82. title = a.xpath(".//@title")
  83. url = a.xpath(".//@href")
  84. if not url or not title or title[0]=="点击隐藏本贴":
  85. continue
  86. url = self.base_url + url[0]
  87. title = title[0]
  88.  
  89. if url in self.urlset:
  90. continue
  91.  
  92. data_list.append([title, url])
  93. self.urlset.add(url)
  94. data = pd.DataFrame(data_list, columns=["title,", "url"])
  95. data.to_csv("./data/database.csv")
  96.  
  97. if __name__ == '__main__':
  98. s = Spider_BaiduTieba()
  99. s.run()

2 清晰数据 和 建立索引部分代码  这里是notebook 完成的, 所以看起来有点奇怪

  1. #%%
  2. import pandas as pd
  3. import sqlite3
  4. import jieba
  5. #%%
  6. data = pd.read_csv("./data/database.csv")
  7. #%%
  8. def check_contain_chinese(check_str):
  9. for ch in check_str:
  10. if u'\u4e00' <= ch <= u'\u9fff':
  11. return True
  12. if "a" <= ch <= "z" or "A" <= ch <= "X":
  13. return True
  14. if "" <= ch <= "":
  15. return True
  16. return False
  17. #%%
  18. data2 = []
  19. for d in data.itertuples():
  20. title = d[1]
  21. url = d[2]
  22. cut = jieba.cut(title)
  23. keyword = ""
  24. for c in cut:
  25. if check_contain_chinese(c):
  26. keyword += " " + c
  27. keyword = keyword.strip()
  28. data2.append([title, keyword, url])
  29. #%%
  30. data3 = pd.DataFrame(data2, columns=["title", "keyword", "url"])
  31. data3
  32. #%%
  33. data3.to_csv("./data/cleaned_database.csv", index=False)
  34. #%%
  35. for line in data3.itertuples():
  36. title, keyword, url = line[1],line[2],line[3]
  37. print(title)
  38. print(keyword)
  39. print(url)
  40. break
  41.  
  42. #%%
  43. conn = sqlite3.connect("./data/database.db")
  44. c = conn.cursor()
  45.  
  46. # 创建数据库
  47. sql = "drop table page_info;"
  48. c.execute(sql)
  49. conn.commit()
  50.  
  51. sql = """
  52. create table page_info(
  53. id INTEGER PRIMARY KEY,
  54. keyword text not null,
  55. url text not null
  56. );
  57. """
  58. c.execute(sql)
  59. conn.commit()
  60.  
  61. # 创建索引表
  62. sql = """
  63. create table page_index(
  64. id INTEGER PRIMARY KEY,
  65. keyword text not null,
  66. page_id INTEGER not null
  67. );
  68. """
  69. c.execute(sql)
  70. conn.commit()
  71. #%%
  72. sql = "delete from page_info;"
  73. c.execute(sql)
  74. conn.commit()
  75.  
  76. # 插入到数据库
  77. i = 0
  78. for line in data3.itertuples():
  79. title, keyword, url = line[1],line[2],line[3]
  80. sql = """
  81. insert into page_info (url, keyword)
  82. values('%s', '%s')
  83. """ % (url, keyword)
  84. c.execute(sql)
  85. conn.commit()
  86. i += 1
  87. if i % 50 == 0:
  88. print(i, len(data3))
  89.  
  90. sql = "delete from page_index;"
  91. c.execute(sql)
  92. conn.commit()
  93.  
  94. sql = "select * from page_info;"
  95. res = c.execute(sql)
  96. res = list(res)
  97. length = len(res)
  98.  
  99. i = 0
  100. for line in res:
  101. pid, words, url = line[0], line[1], line[2]
  102. words = words.split(" ")
  103. for w in words:
  104. sql = """
  105. insert into page_index (keyword, page_id)
  106. values('%s', '%s')
  107. """ % (w, pid)
  108. c.execute(sql)
  109. conn.commit()
  110. i += 1
  111. if i % 100 == 0:
  112. print(i, length)
  113. #%%
  114.  
  115. #%%
  116.  
  117. #%%
  118. titles = list(words)
  119. colums = ["title", "url"] + titles
  120. word_vector = pd.DataFrame(columns=colums)
  121. word_vector
  122. #%%
  123.  
  124. #%%
  125. data = pd.read_csv("./data/database.csv")
  126. #%%
  127. data
  128. #%%
  129. sql = "alter table page_info add title text;"
  130. conn = sqlite3.connect("./data/database.db")
  131. c = conn.cursor()
  132. c.execute(sql)
  133. conn.commit()
  134. #%%
  135. conn = sqlite3.connect("./data/database.db")
  136. c = conn.cursor()
  137. length = len(data)
  138. i = 0
  139. for line in data.itertuples():
  140. pid = line[0]+1
  141. title = line[1]
  142. sql = "UPDATE page_info SET title = '%s' WHERE id = %s "%(title,pid)
  143. try:
  144. c.execute(sql)
  145. conn.commit()
  146. except:
  147. continue
  148. i += 1
  149. if i % 50 == 0:
  150. print(i, length)
  151.  
  152. #%%
  153.  
  154. #%%
  1. 3 web后端 完成检索功能代码
  1. # coding=utf-8
  2. import jieba
  3. import sqlite3
  4. from bottle import route, run, template, request, static_file, redirect
  5.  
  6. @route('/static/<filename>')
  7. def server_static(filename):
  8. if filename == "jquery.min.js":
  9. return static_file("jquery.min.js", root='./data/front/js/')
  10. elif filename == "bootstrap.min.js":
  11. return static_file("bootstrap.js", root='./data/front/js/')
  12. elif filename == "bootstrap.min.css":
  13. return static_file("bootstrap.css", root='./data/front/css/')
  14.  
  15. @route('/')
  16. def index():
  17. return redirect("/hello/")
  18.  
  19. @route('/hello/')
  20. def index():
  21. form = request.GET.decode("utf-8")
  22. keyword = form.get("keyword", "")
  23. cut = list(jieba.cut(keyword))
  24. # 根据索引查询包含关键词的网页编号
  25. page_id_list = get_page_id_list_from_key_word_cut(cut)
  26. # 根据网页编号 查询网页具体内容
  27. page_list = get_page_list_from_page_id_list(page_id_list)
  28. # 根据查询关键字和网页包含的关键字,进行相关度排序 余弦相似度
  29. page_list = sort_page_list(page_list, cut)
  30. context = {
  31. "page_list": page_list[:20],
  32. "keyword": keyword
  33. }
  34. return template("./data/front/searcher.html", context)
  35.  
  36. # 计算page_list中每个page 和 cut的余弦相似度
  37. def sort_page_list(page_list, cut):
  38. con_list = []
  39. for page in page_list:
  40. url = page[2]
  41. words = page[1]
  42. title = page[3]
  43. vector = words.split(" ")
  44. same = 0
  45. for i in vector:
  46. if i in cut:
  47. same += 1
  48. cos = same / (len(vector)*len(cut))
  49. con_list.append([cos, url, words, title])
  50. con_list = sorted(con_list, key=lambda i: i[0], reverse=True)
  51. return con_list
  52.  
  53. # 根据网页id列表获取网页详细内容列表
  54. def get_page_list_from_page_id_list(page_id_list):
  55. id_list = "("
  56. for k in page_id_list:
  57. id_list += "%s,"%k
  58. id_list = id_list.strip(",") + ")"
  59. conn = sqlite3.connect("./data/database.db")
  60. c = conn.cursor()
  61. sql = "select * " \
  62. + "from page_info " \
  63. + "where id in " + id_list + ";"
  64. res = c.execute(sql)
  65. res = [r for r in res]
  66. return res
  67.  
  68. # 根据关键词在索引中获取网页编号
  69. def get_page_id_list_from_key_word_cut(cut):
  70. keyword = "("
  71. for k in cut:
  72. if k == " ":
  73. continue
  74. keyword += "'%s',"%k
  75. keyword = keyword.strip(",") + ")"
  76. conn = sqlite3.connect("./data/database.db")
  77. c = conn.cursor()
  78. sql = "select page_id " \
  79. + "from page_index " \
  80. + "where keyword in " + keyword + ";"
  81. res = c.execute(sql)
  82. res = [r[0] for r in res]
  83. return res
  84.  
  85. if __name__ == '__main__':
  86. run(host='localhost', port=8080)
  1.  
  1.  

python 搭建一个简单的 搜索引擎的更多相关文章

  1. Python实现一个简单三层神经网络的搭建并测试

    python实现一个简单三层神经网络的搭建(有代码) 废话不多说了,直接步入正题,一个完整的神经网络一般由三层构成:输入层,隐藏层(可以有多层)和输出层.本文所构建的神经网络隐藏层只有一层.一个神经网 ...

  2. 用Python写一个简单的Web框架

    一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...

  3. 用nodejs搭建一个简单的服务器

    使用nodejs搭建一个简单的服务器 nodejs优点:性能高(读写文件) 数据操作能力强 官网:www.nodejs.org 验证是否安装成功:cmd命令行中输入node -v 如果显示版本号表示安 ...

  4. 初学Node(六)搭建一个简单的服务器

    搭建一个简单的服务器 通过下面的代码可以搭建一个简单的服务器: var http = require("http"); http.createServer(function(req ...

  5. 【netty】(2)---搭建一个简单服务器

    netty(2)---搭建一个简单服务器 说明:本篇博客是基于学习慕课网有关视频教学.效果:当用户访问:localhost:8088 后 服务器返回 "hello netty"; ...

  6. 用Python编写一个简单的Http Server

    用Python编写一个简单的Http Server Python内置了支持HTTP协议的模块,我们可以用来开发单机版功能较少的Web服务器.Python支持该功能的实现模块是BaseFTTPServe ...

  7. 使用gitblit搭建一个简单的局域网服务器

    使用gitblit搭建一个简单的局域网服务器 1.使用背景 现在很多使用github管理代码,但是github需要互联网的支持,而且私有的git库需要收费.有一些项目的代码不能外泄,所以,搭建一个局域 ...

  8. Golang学习-第二篇 搭建一个简单的Go Web服务器

    序言 由于本人一直从事Web服务器端的程序开发,所以在学习Golang也想从Web这里开始学起,如果对Golang还不太清楚怎么搭建环境的朋友们可以参考我的上一篇文章 Golang的简单介绍及Wind ...

  9. Prism for WPF 搭建一个简单的模块化开发框架 (一个节点)

    原文:Prism for WPF 搭建一个简单的模块化开发框架 (一个节点) 这里我就只贴图不贴代码了,看看这个节点之前的效果 觉得做的好的地方可以范之前的文章看看 有好的建议也可以说说   填充数据 ...

随机推荐

  1. 2017/05/04 java 基础 随笔

    1.java变量在使用之前必须初始化 int  a; a=10 ; int b; 没有初始化,也没有使用也不报错 2.强制类型转换  int a=8: byte b=6; b=(byte)(a+b); ...

  2. 【比赛游记】FJOI2019瞎打记

    \(\mathrm{day}\) \(-4\) 又是睡到中午才起来,这样下去省选会睡迟的. 然后下午在补 WF2019 的题目,很快就能补完的(大雾). \(\mathrm{day}\) \(-3\) ...

  3. Pytorch 资料汇总(持续更新)

    1. Pytorch 论坛/网站 PyTorch 中文网 python优先的深度学习框架 Pytorch中文文档 Pythrch-CN文档地址 PyTorch 基礎篇 2. Pytorch 书籍 深度 ...

  4. 【Python学习笔记】调整matplotlib的图例legend的位置

    有时默认的图例位置不符合我们的需要,那么我们可以使用下面的代码对legend位置进行调整. plt.legend(loc='String or Number', bbox_to_anchor=(num ...

  5. UML和模式应用3:迭代和进化式分析和设计案例研究

    1.前言 如何进行迭代和进化式分析和设计?将采用案例研究的方式贯穿始终.案例研究所包含的内容: UI元素 核心应用逻辑层 数据库访问 与外部软硬构件的协作 本章关于OOA/D主要介绍核心应用逻辑层 2 ...

  6. __ATTR引发的编译错误【原创】

    有一天我编译内核模块驱动的时候发现如下错误 Linux kernel版本:4.1.15 error: negative width in bit-field '<anonymous>' 代 ...

  7. 重装系统windows

    1 boot没有usb启动选项,驱动没加载好,先进入xp系统正确安装usb驱动,重启 2 file not fount bootmgr 用pe自带修复程序修复,修复驱动盘符为最小那个,非c盘 3 wi ...

  8. Intellij IDEA14 搜索框及控制台乱码解决

    搜索ctrl+F及ctrl+H的搜索框.调试的时候控制台.导入module都显示为为中文乱码 如下: 解决方案: File->Setting->IDE Settings->Appea ...

  9. jquery load加载页面内ajax返回的div不能响应页面js的问题的解决方案

    1. 前言 由于项目需要,需要load一个页面并保持ajax返回的div能响应其页面内的JS的click事件.这个不是 解决用jquery load加载页面到div时,不执行页面js的问题 这类问题, ...

  10. Java连接oracle数据库的两种常用方法

    1. 使用thin连接 由于thin驱动都是纯Java代码,并且使用TCP/IP技术通过java的Socket连接上Oracle数据库,所以thin驱动是与平台无关的,你无需安装Oracle客户端,只 ...