python 手把手教你基于搜索引擎实现文章查重

前言

文章抄袭在互联网中普遍存在,很多博主都收受其烦。近几年随着互联网的发展,抄袭等不道德行为在互联网上愈演愈烈,甚至复制、黏贴后发布标原创屡见不鲜,部分抄袭后的文章甚至标记了一些联系方式从而使读者获取源码等资料。这种恶劣的行为使人愤慨。

本文使用搜索引擎结果作为文章库,再与本地或互联网上数据做相似度对比,实现文章查重;由于查重的实现过程与一般情况下的微博情感分析实现流程相似,从而轻易的扩展出情感分析功能(下一篇将在此篇代码的基础上完成数据采集、清洗到情感分析的整个过程)。

由于近期时间上并不充裕,暂时实现了主要功能,细节上并没有进行优化,但是在代码结构上进行了一些简要的设计,使得之后的功能扩展、升级更为简便。我本人也将会持续更新该工具的功能,争取让这个工具在技术上更加的成熟、实用。

技术

本文实现的查重功能为了考虑适配大多数站点,从而使用selenium用作数据获取,配置不同搜索引擎的信息,实现较为通用的搜索引擎查询,并且不需要考虑过多的动态数据抓取;分词主要使用jieba库,完成对中文语句的分词;使用余弦相似度完成文本相似度的对比并导出对比数据至Excel文章留作举报信息。

微博情感分析基于sklearn,使用朴素贝叶斯完成对数据的情感分析;在数据抓取上,实现流程与文本查重的功能类似。

测试代码获取

CSDN codechina 代码仓库:https://codechina.csdn.net/A757291228/s-analysetooldemo

环境

作者的环境说明如下:

  • 操作系统:Windows7 SP1 64
  • python 版本:3.7.7
  • 浏览器:谷歌浏览器
  • 浏览器版本: 80.0.3987 (64 位)

如有错误欢迎指出,欢迎留言交流。

一、实现文本查重

1.1 selenium安装配置

由于使用的selenium,在使用前需要确保读者是否已安装selenium,使用pip命令,安装如下:

pip install selenium

安装完成 Selenium 还需要下载一个驱动。

  • 谷歌浏览器驱动:驱动版本需要对应浏览器版本,不同的浏览器使用对应不同版本的驱动,点击下载
  • 如果是使用火狐浏览器,查看火狐浏览器版本,点击
    GitHub火狐驱动下载地址
    下载(英文不好的同学右键一键翻译即可,每个版本都有对应浏览器版本的使用说明,看清楚下载即可)

安装了selenium后新建一python文件名为selenium_search,先在代码中引入

from selenium import webdriver

可能有些读者没有把驱动配置到环境中,接下来我们可以指定驱动的位置(博主已配置到环境中):

driver = webdriver.Chrome(executable_path=r'F:\python\dr\chromedriver_win32\chromedriver.exe')

新建一个变量url赋值为百度首页链接,使用get方法传入url地址,尝试打开百度首页,完整代码如下:

from selenium import webdriverurl='https://www.baidu.com'
driver=webdriver.Chrome()
driver.get(url)

在小黑框中使用命令行运行python文件(windows下):
在这里插入图片描述
运行脚本后将会打开谷歌浏览器并跳转至百度首页:
在这里插入图片描述
这样就成功使用selenium打开了指定网址,接下来将指定搜索关键词查询得到结果,再从结果中遍历到相似数据。

1.2 selenium百度搜索引擎关键词搜索

在自动操控浏览器进行关键字键入到搜索框前,需要获取搜索框元素对象。使用谷歌浏览器打开百度首页,右键搜索框选择查看,将会弹出网页元素(代码)查看视窗,找到搜索框元素(使用鼠标在元素节点中移动,鼠标当前位置的元素节点将会对应的在网页中标蓝):
在这里插入图片描述
在html代码中,id的值大多数情况下唯一(除非是打错了),在此选择id作为获取搜索框元素对象的标记。selenium提供了find_element_by_id方法,可以通过传入id获取到网页元素对象。

input=driver.find_element_by_id('kw')

获取元素对象后,使用send_keys方法可传入需要键入的值:

input.send_keys('php基础教程 第十一步 面向对象')

在此我传入了 “php基础教程 第十一步 面向对象”作为关键字作为搜索。运行脚本查看是否在搜索框中键入了关键字。代码如下:

input.send_keys('php基础教程 第十一步 面向对象')

成功打开浏览器并键入了搜索关键字:
在这里插入图片描述
现在还差点击“百度一下”按钮完成最终的搜索。使用与查看搜索框相同的元素查看方法查找“百度一下”按钮的id值:
在这里插入图片描述
使用find_element_by_id方法获取到该元素对象,随后使用click方法使该按钮完成点击操作:

search_btn=driver.find_element_by_id('su')
search_btn.click()

完整代码如下:

from selenium import webdriverurl='https://www.baidu.com'
driver=webdriver.Chrome()
driver.get(url)
input=driver.find_element_by_id('kw')
input.send_keys('php基础教程 第十一步 面向对象')
search_btn=driver.find_element_by_id('su')
search_btn.click()

浏览器自动完成了键入搜索关键字及搜索功能:
在这里插入图片描述

1.3 搜索结果遍历

当前已在浏览器中得到了搜索结果,接下来需要获取整个web页面内容,得到搜索结果。使用selenium并不能很方便的获取到,在这里使用BeautifulSoup对整个web页面进行解析并获取搜索结果。

BeautifulSoup是一个HTML/XML解析器,使用BeautifulSoup会极大的方便我们对整个html的信息获取。
使用BeautifulSoup前需确保已安装。安装命令如下:

pip install BeautifulSoup

安装后,在当前python文件头部引入:

from bs4 import BeautifulSoup

获取html文本可以调用page_source即可:

html=driver.page_source

得到了html代码后,新建BeautifulSoup对象,传入html内容并且指定解析器,这里指定使用 html.parser 解析器:

soup = BeautifulSoup(html, "html.parser")

接下来查看搜索内容,发现所有的结果都由一个h标签包含,并且classt
在这里插入图片描述
BeautifulSoup提供了select方法对标签进行获取,支持通过类名、标签名、id、属性、组合查找等。我们发现百度搜索结果中,结果皆有一个class =“t”,此时可以通过类名进行遍历获取最为简便:

search_res_list=soup.select('.t')

select方法中传入类名t,在类名前加上一个点(.)表示是通过类名获取元素。
完成这一步后可以添加print尝试打印出结果:

print(search_res_list)

一般情况下,可能输出search_res_list为空列表,这是因为我们在浏览器解析数据渲染到浏览器前已经获取了浏览器当前页的内容,这时有一个简单的方法可以解决这个问题,但是此方法效率却不高,在此只是暂时使用,之后将会用其它效率高于此方法的代码替换(使用time需要在头部引入):

time.sleep(2)

完整代码如下:

from selenium import webdriver
from bs4 import BeautifulSoup
import timeurl='https://www.baidu.com'
driver=webdriver.Chrome()
driver.get(url)
input=driver.find_element_by_id('kw')
input.send_keys('php基础教程 第十一步 面向对象')
search_btn=driver.find_element_by_id('su')
search_btn.click()time.sleep(2)#在此等待 使浏览器解析并渲染到浏览器html=driver.page_source #获取网页内容
soup = BeautifulSoup(html, "html.parser")
search_res_list=soup.select('.t')
print(search_res_list)

运行程序将会输出内容:
在这里插入图片描述
获取到的结果为所有class为t的标签,包括该标签的子节点,并且使用点(.)运算发可以获取子节点元素。通过浏览器得到的搜索内容皆为链接,点击可跳转,那么只需要获取每一个元素下的a标签即可:

for el in search_res_list:print(el.a)

在这里插入图片描述
从结果中很明显的看出搜索结果的a标签已经获取,那么接下来我们需要的是提取每个a标签内的href超链接。获取href超链接直接使用列表获取元素的方式获取即可:

for el in search_res_list:print(el.a['href'])

运行脚本成功得到结果:
在这里插入图片描述
细心的读者可能会发现,这些获取到的结果中,都是baidu的网址。其实这些网址可以说是“索引”,通过这些索引再次跳转到真实网址。由于这些“索引”不一定会变动,并不利于长期存储,在此还是需要获取到真实的链接。
我们调用js脚本对这些网址进行访问,这些网址将会跳转到真实网址,跳转后再获取当前的网址信息即可。调用execute_script方法可执行js代码,代码如下:

for el in search_res_list:js = 'window.open("'+el.a['href']+'")'driver.execute_script(js)

打开新的网页后,需要获取新网页的句柄,否则无法操控新网页。获取句柄的方法如下:

handle_this=driver.current_window_handle#获取当前句柄
handle_all=driver.window_handles#获取所有句柄

获取句柄后需要把当前操作的对象切换成新的页面。由于打开一个页面后所有页面只有2个,简单的使用遍历做一个替换:

handle_exchange=None#要切换的句柄
for handle in handle_all:#不匹配为新句柄if handle != handle_this:#不等于当前句柄就交换handle_exchange = handle
driver.switch_to.window(handle_exchange)#切换

切换后,操作对象为当前刚打开的页面。通过current_url属性拿到新页面的url:

real_url=driver.current_url
print(real_url)

随后关闭当前页面,把操作对象置为初始页面:

driver.close()
driver.switch_to.window(handle_this)#换回最初始界面

运行脚本成功获取到真实url:
在这里插入图片描述
最后在获取到真实url后使用一个列表将结果存储:

real_url_list.append(real_url)

这一部分完整代码如下:

from selenium import webdriver
from bs4 import BeautifulSoup
import timeurl='https://www.baidu.com'
driver=webdriver.Chrome()
driver.get(url)
input=driver.find_element_by_id('kw')
input.send_keys('php基础教程 第十一步 面向对象')
search_btn=driver.find_element_by_id('su')
search_btn.click()time.sleep(2)#在此等待 使浏览器解析并渲染到浏览器html=driver.page_source
soup = BeautifulSoup(html, "html.parser")
search_res_list=soup.select('.t')real_url_list=[]
# print(search_res_list)
for el in search_res_list:js = 'window.open("'+el.a['href']+'")'driver.execute_script(js)handle_this=driver.current_window_handle#获取当前句柄handle_all=driver.window_handles#获取所有句柄handle_exchange=None#要切换的句柄for handle in handle_all:#不匹配为新句柄if handle != handle_this:#不等于当前句柄就交换handle_exchange = handledriver.switch_to.window(handle_exchange)#切换real_url=driver.current_urlprint(real_url)real_url_list.append(real_url)#存储结果driver.close()driver.switch_to.window(handle_this)

1.4 获取源文本

在当前文件的目录下新建一个文件夹,命名为textsrc,在该目录下创建一个txt文件,把需要对比的文本存放至该文本中。在此我存放的内容为文章“php基础教程 第十一步 面向对象”的内容。
在这里插入图片描述
在代码中编写一个函数为获取文本内容:

def read_txt(path=''):f = open(path,'r')return f.read()
src=read_txt(r'F:\tool\textsrc\src.txt')

为了方便测试,这里使用是绝对路径。
获取到文本内容后,编写余弦相似度的对比方法。

1.5 余弦相似度

相似度计算参考文章《python实现余弦相似度文本比较》,本人修改一部分从而实现。

本文相似度对比使用余弦相似度算法,一般步骤分为分词->向量计算->计算相似度。
新建一个python文件,名为Analyse。新建一个类名为Analyse,在类中添加分词方法,并在头部引入jieba分词库,以及collections统计次数:

from jieba import lcut
import jieba.analyse
import collections

Count方法:

#分词
def Count(self,text):tag = jieba.analyse.textrank(text,topK=20)word_counts = collections.Counter(tag) #计数统计return word_counts

Count方法接收一个text变量,text变量为文本,使用textrank方法分词并且使用Counter计数。
随后添加MergeWord方法,使词合并方便之后的向量计算:

#词合并
def MergeWord(self,T1,T2):MergeWord = []for i in T1:MergeWord.append(i)for i in T2:if i not in MergeWord:MergeWord.append(i)return MergeWord

合并方法很简单不再做解释。接下来添加向量计算方法:

# 得出文档向量
def CalVector(self,T1,MergeWord):TF1 = [0] * len(MergeWord)for ch in T1:TermFrequence = T1[ch]word = chif word in MergeWord:TF1[MergeWord.index(word)] = TermFrequencereturn TF1

最后添加相似度计算方法:

def cosine_similarity(self,vector1, vector2):dot_product = 0.0normA = 0.0normB = 0.0for a, b in zip(vector1, vector2):#两个向量组合成 [(1, 4), (2, 5), (3, 6)] 最短形式表现dot_product += a * b    normA += a ** 2normB += b ** 2if normA == 0.0 or normB == 0.0:return 0else:return round(dot_product / ((normA**0.5)*(normB**0.5))*100, 2)

相似度方法接收两个向量,随后计算相似度并返回。为了代码冗余度少,在这里先简单的添加一个方法,完成计算流程:

def get_Tfidf(self,text1,text2):#测试对比本地数据对比搜索引擎方法# self.correlate.word.set_this_url(url)T1 = self.Count(text1)T2 = self.Count(text2)mergeword = self.MergeWord(T1,T2)return self.cosine_similarity(self.CalVector(T1,mergeword),self.CalVector(T2,mergeword))

Analyse类的完整代码如下:

from jieba import lcut
import jieba.analyse
import collectionsclass Analyse:def get_Tfidf(self,text1,text2):#测试对比本地数据对比搜索引擎方法# self.correlate.word.set_this_url(url)T1 = self.Count(text1)T2 = self.Count(text2)mergeword = self.MergeWord(T1,T2)return self.cosine_similarity(self.CalVector(T1,mergeword),self.CalVector(T2,mergeword))#分词def Count(self,text):tag = jieba.analyse.textrank(text,topK=20)word_counts = collections.Counter(tag) #计数统计return word_counts#词合并def MergeWord(self,T1,T2):MergeWord = []for i in T1:MergeWord.append(i)for i in T2:if i not in MergeWord:MergeWord.append(i)return MergeWord# 得出文档向量def CalVector(self,T1,MergeWord):TF1 = [0] * len(MergeWord)for ch in T1:TermFrequence = T1[ch]word = chif word in MergeWord:TF1[MergeWord.index(word)] = TermFrequencereturn TF1#计算 TF-IDFdef cosine_similarity(self,vector1, vector2):dot_product = 0.0normA = 0.0normB = 0.0for a, b in zip(vector1, vector2):#两个向量组合成 [(1, 4), (2, 5), (3, 6)] 最短形式表现dot_product += a * b    normA += a ** 2normB += b ** 2if normA == 0.0 or normB == 0.0:return 0else:return round(dot_product / ((normA**0.5)*(normB**0.5))*100, 2)

1.6 搜索结果内容与文本做相似度对比

在selenium_search文件中引入Analyse,并且新建对象:

from Analyse import Analyse
Analyse=Analyse()

在遍历搜索结果中添加获取新打开后的页面的网页内容:

time.sleep(5)
html_2=driver.page_source

使用 time.sleep(5)是为了等待浏览器能够有时间渲染当前web内容。获取到新打开的页面内容后,进行相似度对比:

Analyse.get_Tfidf(src,html_2)

由于返回的是一个值,使用print输出:

print('相似度:',Analyse.get_Tfidf(src,html_2))

完整代码如下:

from selenium import webdriver
from bs4 import BeautifulSoup
import time
from Analyse import Analysedef read_txt(path=''):f = open(path,'r')return f.read()#获取对比文件
src=read_txt(r'F:\tool\textsrc\src.txt')
Analyse=Analyse()url='https://www.baidu.com'
driver=webdriver.Chrome()
driver.get(url)
input=driver.find_element_by_id('kw')
input.send_keys('php基础教程 第十一步 面向对象')
search_btn=driver.find_element_by_id('su')
search_btn.click()time.sleep(2)#在此等待 使浏览器解析并渲染到浏览器html=driver.page_source
soup = BeautifulSoup(html, "html.parser")
search_res_list=soup.select('.t')real_url_list=[]
# print(search_res_list)
for el in search_res_list:js = 'window.open("'+el.a['href']+'")'driver.execute_script(js)handle_this=driver.current_window_handle#获取当前句柄handle_all=driver.window_handles#获取所有句柄handle_exchange=None#要切换的句柄for handle in handle_all:#不匹配为新句柄if handle != handle_this:#不等于当前句柄就交换handle_exchange = handledriver.switch_to.window(handle_exchange)#切换real_url=driver.current_urltime.sleep(5)html_2=driver.page_sourceprint('相似度:',Analyse.get_Tfidf(src,html_2))print(real_url)real_url_list.append(real_url)driver.close()driver.switch_to.window(handle_this)

运行脚本:
在这里插入图片描述
结果显示有几个高度相似的链接,那么这几个就是疑似抄袭的文章了。
以上是完成基本查重的代码,但是相对于说代码比较冗余、杂乱,接下来我们优化一下代码。

二、代码优化

通过以上的程序编程,简要步骤可以分为:获取搜索内容->获取结果->计算相似度。我们可以新建三个类,分别为:Browser、Analyse(已新建)、SearchEngine。
Browser用于搜索、数据获取等;Analyse用于相似度分析、向量计算等;SearchEngine用于不同搜索引擎的基本配置,因为大部分搜多引擎的搜索方式较为一致。

2.1Browser 类

初始化
新建一个python文件,名为Browser,添加初始化方法:

def __init__(self,conf):self.browser=webdriver.Chrome()self.conf=confself.engine_conf=EngineConfManage().get_Engine_conf(conf['engine']).get_conf()

self.browser=webdriver.Chrome()为新建一个浏览器对象;conf为传入的搜索配置,之后进行搜索内容由编写配置字典实现;self.engine_conf=EngineConfManage().get_Engine_conf(conf['engine']).get_conf()为获取搜索引擎的配置,不同搜索引擎的输入框、搜索按键不一致,通过不同的配置信息实现多搜索引擎搜索。

添加搜索方法

	#搜索内容写入到搜素引擎中def send_keyword(self):input = self.browser.find_element_by_id(self.engine_conf['searchTextID'])input.send_keys(self.conf['kw'])

以上方法中self.engine_conf['searchTextID']self.conf['kw']通过初始化方法得到对应的搜索引擎配置信息,直接获取信息得到元素。

点击搜索

	#搜索框点击def click_search_btn(self):search_btn = self.browser.find_element_by_id(self.engine_conf['searchBtnID'])search_btn.click()

通过使用self.engine_conf['searchBtnID']获取搜索按钮的id。

获取搜索结果与文本

#获取搜索结果与文本def get_search_res_url(self):res_link={}WebDriverWait(self.browser,timeout=30,poll_frequency=1).until(EC.presence_of_element_located((By.ID, "page")))#内容通过 BeautifulSoup 解析content=self.browser.page_sourcesoup = BeautifulSoup(content, "html.parser")search_res_list=soup.select('.'+self.engine_conf['searchContentHref_class'])for el in search_res_list:js = 'window.open("'+el.a['href']+'")'self.browser.execute_script(js)handle_this=self.browser.current_window_handle  #获取当前句柄handle_all=self.browser.window_handles          #获取所有句柄handle_exchange=None                            #要切换的句柄for handle in handle_all:                       #不匹配为新句柄if handle != handle_this:                   #不等于当前句柄就交换handle_exchange = handleself.browser.switch_to.window(handle_exchange)  #切换real_url=self.browser.current_urltime.sleep(1)res_link[real_url]=self.browser.page_source     #结果获取self.browser.close()self.browser.switch_to.window(handle_this)return res_link

以上方法跟之前编写的遍历搜索结果内容相似,从中添加了WebDriverWait(self.browser,timeout=30,poll_frequency=1).until(EC.presence_of_element_located((By.ID, "page")))替代了sleep,用于判断EC.presence_of_element_located((By.ID, "page"))是否找到id值为page的网页元素,idpage的网页元素为分页按钮的标签id,如果未获取表示当前web页并未加载完全,等待时间为timeout=3030秒,如果已过去则跳过等待。
以上代码中并不做相似度对比,而是通过 res_link[real_url]=self.browser.page_source 将内容与url存入字典,随后返回,之后再做相似度对比,这样编写利于之后的功能扩展。

打开目标搜索引擎进行搜索

	#打开目标搜索引擎进行搜索def search(self):self.browser.get(self.engine_conf['website'])       #打开搜索引擎站点self.send_keyword()                                 #输入搜索kwself.click_search_btn()                             #点击搜索return self.get_search_res_url()                    #获取web页搜索数据

最后添加一个search方法,直接调用search方法即可实现之前的所有操作,不用暴露过多简化使用。
完整代码如下:

from selenium import webdriver
from bs4 import BeautifulSoup
from SearchEngine import EngineConfManage
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import timeclass Browser:def __init__(self,conf):self.browser=webdriver.Chrome()self.conf=confself.engine_conf=EngineConfManage().get_Engine_conf(conf['engine']).get_conf()#搜索内容写入到搜素引擎中def send_keyword(self):input = self.browser.find_element_by_id(self.engine_conf['searchTextID'])input.send_keys(self.conf['kw'])#搜索框点击def click_search_btn(self):search_btn = self.browser.find_element_by_id(self.engine_conf['searchBtnID'])search_btn.click()#获取搜索结果与文本def get_search_res_url(self):res_link={}WebDriverWait(self.browser,timeout=30,poll_frequency=1).until(EC.presence_of_element_located((By.ID, "page")))#内容通过 BeautifulSoup 解析content=self.browser.page_sourcesoup = BeautifulSoup(content, "html.parser")search_res_list=soup.select('.'+self.engine_conf['searchContentHref_class'])for el in search_res_list:js = 'window.open("'+el.a['href']+'")'self.browser.execute_script(js)handle_this=self.browser.current_window_handle  #获取当前句柄handle_all=self.browser.window_handles          #获取所有句柄handle_exchange=None                            #要切换的句柄for handle in handle_all:                       #不匹配为新句柄if handle != handle_this:                   #不等于当前句柄就交换handle_exchange = handleself.browser.switch_to.window(handle_exchange)  #切换real_url=self.browser.current_urltime.sleep(1)res_link[real_url]=self.browser.page_source     #结果获取self.browser.close()self.browser.switch_to.window(handle_this)return res_link#打开目标搜索引擎进行搜索def search(self):self.browser.get(self.engine_conf['website'])       #打开搜索引擎站点self.send_keyword()                                 #输入搜索kwself.click_search_btn()                             #点击搜索return self.get_search_res_url()                    #获取web页搜索数据

2.2SearchEngine 类

SearchEngine类主要用于不同搜索引擎的配置编写。更加简便的实现搜索引擎或相似业务的扩展。

#搜索引擎配置
class EngineConfManage:def get_Engine_conf(self,engine_name):if engine_name=='baidu':return BaiduEngineConf()elif engine_name=='qihu360':return Qihu360EngineConf()elif engine_name=='sougou':return SougouEngineConf()class EngineConf:def __init__(self):self.engineConf={}def get_conf(self):return self.engineConfclass BaiduEngineConf(EngineConf):engineConf={}def __init__(self):self.engineConf['searchTextID']='kw'self.engineConf['searchBtnID']='su'self.engineConf['nextPageBtnID_xpath_f']='//*[@id="page"]/div/a[10]'self.engineConf['nextPageBtnID_xpath_s']='//*[@id="page"]/div/a[11]'self.engineConf['searchContentHref_class']='t'self.engineConf['website']='http://www.baidu.com'class Qihu360EngineConf(EngineConf):def __init__(self):passclass SougouEngineConf(EngineConf):def __init__(self):pass

在此只实现了百度搜索引擎的配置编写。所有不同种类的搜索引擎继承EngineConf基类,使子类都有了get_conf方法。EngineConfManage类用于不同搜索引擎的调用,传入引擎名即可。

2.3如何使用

首先引入两个类:

from Browser import Browser
from Analyse import Analyse

新建一个方法读取本地文件:

def read_txt(path=''):f = open(path,'r')return f.read()

获取文件并新建数据分析类:

src=read_txt(r'F:\tool\textsrc\src.txt')#获取本地文本
Analyse=Analyse()

配置信息字典编写:

#配置信息
conf={'kw':'php基础教程 第十一步 面向对象','engine':'baidu',}

新建Browser类,并传入配置信息:

drvier=Browser(conf)

获取搜索结果及内容

url_content=drvier.search()#获取搜索结果及内容

遍历结果及计算相似度:

for k in url_content:print(k,'相似度:',Analyse.get_Tfidf(src,url_content[k]))

完整代码如下:

from Browser import Browser
from Analyse import Analysedef read_txt(path=''):f = open(path,'r')return f.read()src=read_txt(r'F:\tool\textsrc\src.txt')#获取本地文本
Analyse=Analyse()#配置信息
conf={'kw':'php基础教程 第十一步 面向对象','engine':'baidu',}drvier=Browser(conf)
url_content=drvier.search()#获取搜索结果及内容
for k in url_content:print(k,'相似度:',Analyse.get_Tfidf(src,url_content[k]))

是不是感觉舒服多了?简直不要太清爽。你以为这就完了吗?还没完,接下来扩展一下功能。

三、功能扩展

暂时这个小工具的功能只有查重这个基础功能,并且这个存在很多问题。如没有白名单过滤、只能查一篇文章的相似度、如果比较懒也没有直接获取文章列表自动查重的功能以及结果导出等。接下来慢慢完善部分功能,由于篇幅关系并不完全把的功能实现在此列出,之后将会持续更新。

3.1自动获取文本

新建一个python文件,名为FileHandle。该类用于自动获取指定目录下txt文件,txt文件文件名为关键字,内容为该名称的文章内容。类代码如下:

import osclass FileHandle:#获取文件内容def get_content(self,path):f = open(path,"r")   #设置文件对象content = f.read()     #将txt文件的所有内容读入到字符串str中f.close()   #将文件关闭return content#获取文件内容def get_text(self):file_path=os.path.dirname(__file__)                                 #当前文件所在目录txt_path=file_path+r'\textsrc'                                      #txt目录rootdir=os.path.join(txt_path)                                      #目标目录内容local_text={}# 读txt 文件for (dirpath,dirnames,filenames) in os.walk(rootdir):for filename in filenames:if os.path.splitext(filename)[1]=='.txt':flag_file_path=dirpath+'\\'+filename                    #文件路径flag_file_content=self.get_content(flag_file_path) #读文件路径if flag_file_content!='':local_text[filename.replace('.txt', '')]=flag_file_content  #键值对内容return local_text

其中有两个方法get_contentget_textget_text为获取目录下所有txt文件路径,通过get_content获取到详细文本内容,返回local_textlocal_text键为文件名,值为文本内容。

3.2BrowserManage类

Browser类文件中添加一个BrowserManage类继承于Browser,添加方法:

#打开目标搜索引擎进行搜索def search(self):self.browser.get(self.engine_conf['website'])       #打开搜索引擎站点self.send_keyword()                                 #输入搜索kwself.click_search_btn()                             #点击搜索return self.get_search_res_url()                    #获取web页搜索数据

添加该类使Browser类的逻辑与其它方法分开,便于扩展。

3.3Browser类的扩展

Browser类中添加下一页方法,使搜索内容时能够获取更多内容,并且可指定获取结果条数:

#下一页def click_next_page(self,md5):WebDriverWait(self.browser,timeout=30,poll_frequency=1).until(EC.presence_of_element_located((By.ID, "page")))#百度搜索引擎翻页后下一页按钮 xpath 不一致 默认非第一页xpathtry:next_page_btn = self.browser.find_element_by_xpath(self.engine_conf['nextPageBtnID_xpath_s'])except:next_page_btn = self.browser.find_element_by_xpath(self.engine_conf['nextPageBtnID_xpath_f'])next_page_btn.click()#md5 进行 webpag text 对比,判断是否已翻页 (暂时使用,存在bug)i=0while md5==hashlib.md5(self.browser.page_source.encode(encoding='UTF-8')).hexdigest():#md5 对比time.sleep(0.3)#防止一些错误,暂时使用强制停止保持一些稳定i+=1if i>100:return Falsereturn True

百度搜索引擎翻页后下一页按钮 xpath 不一致 默认非第一页xpath,出现异常使用另外一个xpath。随后对页面进行md5,对比md5值,如果当前页面没有刷新,md5值将不会改变,等待小短时间之后点击下一页。

3.4get_search_res_url方法的修改

get_search_res_url方法的修改了部分内容,添加了增加结果条数指定、下一页内容获取以及白名单设置更改过后的代码如下:

#获取搜索结果与文本def get_search_res_url(self):res_link={}WebDriverWait(self.browser,timeout=30,poll_frequency=1).until(EC.presence_of_element_located((By.ID, "page")))#内容通过 BeautifulSoup 解析content=self.browser.page_sourcesoup = BeautifulSoup(content, "html.parser")search_res_list=soup.select('.'+self.engine_conf['searchContentHref_class'])while len(res_link)<self.conf['target_page']:for el in search_res_list:js = 'window.open("'+el.a['href']+'")'self.browser.execute_script(js)handle_this=self.browser.current_window_handle  #获取当前句柄handle_all=self.browser.window_handles          #获取所有句柄handle_exchange=None                            #要切换的句柄for handle in handle_all:                       #不匹配为新句柄if handle != handle_this:                   #不等于当前句柄就交换handle_exchange = handleself.browser.switch_to.window(handle_exchange)  #切换real_url=self.browser.current_urlif real_url in self.conf['white_list']:         #白名单continuetime.sleep(1)res_link[real_url]=self.browser.page_source     #结果获取self.browser.close()self.browser.switch_to.window(handle_this)content_md5=hashlib.md5(self.browser.page_source.encode(encoding='UTF-8')).hexdigest() #md5对比self.click_next_page(content_md5)return res_link

while len(res_link)<self.conf['target_page']:为增加了对结果条数的判断。

content_md5=hashlib.md5(self.browser.page_source.encode(encoding='UTF-8')).hexdigest() #md5对比
self.click_next_page(content_md5)

以上代码增加了当前页面刷新后的md5值判断,不一致则进行跳转。

if real_url in self.conf['white_list']:         #白名单continue

以上代码对白名单进行了判断,自己设置的白名单不加入到条数。

3.5新建Manage类

新建一python文件名为Manage,再次封装。代码如下:

from Browser import BrowserManage
from Analyse import Analyse
from FileHandle import FileHandleclass Manage:def __init__(self,conf):self.drvier=BrowserManage(conf)self.textdic=FileHandle().get_text()self.analyse=Analyse()def get_local_analyse(self):    resdic={}for k in self.textdic:res={}self.drvier.set_kw(k)url_content=self.drvier.search()#获取搜索结果及内容for k1 in url_content:res[k1]=self.analyse.get_Tfidf(self.textdic[k],url_content[k1])resdic[k]=resreturn resdic

以上代码初始化方法接收一个参数,且初始化方法中新建了BrowserManage对象、Analyse对象以及获取了文本内容。
get_local_analyse方法遍历文本,使用文件名当作关键字进行搜索,并且将搜索内容与当前文本做相似度对比,最后返回结果。
结果如下:
在这里插入图片描述
博主目录下文件如下:
在这里插入图片描述
相似度分析部分以上为主要内容,工具之后将会丢GitHubcsdn的代码仓库中,使用的无头模式,本篇所讲的内容为一般实现。

所有完整的代码如下

Analyse类:

from jieba import lcut
import jieba.analyse
import collections
from FileHandle import FileHandleclass Analyse:def get_Tfidf(self,text1,text2):#测试对比本地数据对比搜索引擎方法# self.correlate.word.set_this_url(url)T1 = self.Count(text1)T2 = self.Count(text2)mergeword = self.MergeWord(T1,T2)return self.cosine_similarity(self.CalVector(T1,mergeword),self.CalVector(T2,mergeword))#分词def Count(self,text):tag = jieba.analyse.textrank(text,topK=20)word_counts = collections.Counter(tag) #计数统计return word_counts#词合并def MergeWord(self,T1,T2):MergeWord = []for i in T1:MergeWord.append(i)for i in T2:if i not in MergeWord:MergeWord.append(i)return MergeWord# 得出文档向量def CalVector(self,T1,MergeWord):TF1 = [0] * len(MergeWord)for ch in T1:TermFrequence = T1[ch]word = chif word in MergeWord:TF1[MergeWord.index(word)] = TermFrequencereturn TF1#计算 TF-IDFdef cosine_similarity(self,vector1, vector2):dot_product = 0.0normA = 0.0normB = 0.0for a, b in zip(vector1, vector2):#两个向量组合成 [(1, 4), (2, 5), (3, 6)] 最短形式表现dot_product += a * b    normA += a ** 2normB += b ** 2if normA == 0.0 or normB == 0.0:return 0else:return round(dot_product / ((normA**0.5)*(normB**0.5))*100, 2)

Browser类:

from selenium import webdriver
from bs4 import BeautifulSoup
from SearchEngine import EngineConfManage
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import hashlib
import time
import xlwtclass Browser:def __init__(self,conf):self.browser=webdriver.Chrome()self.conf=confself.conf['kw']=''self.engine_conf=EngineConfManage().get_Engine_conf(conf['engine']).get_conf()#搜索内容设置def set_kw(self,kw):self.conf['kw']=kw#搜索内容写入到搜素引擎中def send_keyword(self):input = self.browser.find_element_by_id(self.engine_conf['searchTextID'])input.send_keys(self.conf['kw'])#搜索框点击def click_search_btn(self):search_btn = self.browser.find_element_by_id(self.engine_conf['searchBtnID'])search_btn.click()#获取搜索结果与文本def get_search_res_url(self):res_link={}WebDriverWait(self.browser,timeout=30,poll_frequency=1).until(EC.presence_of_element_located((By.ID, "page")))#内容通过 BeautifulSoup 解析content=self.browser.page_sourcesoup = BeautifulSoup(content, "html.parser")search_res_list=soup.select('.'+self.engine_conf['searchContentHref_class'])while len(res_link)<self.conf['target_page']:for el in search_res_list:js = 'window.open("'+el.a['href']+'")'self.browser.execute_script(js)handle_this=self.browser.current_window_handle  #获取当前句柄handle_all=self.browser.window_handles          #获取所有句柄handle_exchange=None                            #要切换的句柄for handle in handle_all:                       #不匹配为新句柄if handle != handle_this:                   #不等于当前句柄就交换handle_exchange = handleself.browser.switch_to.window(handle_exchange)  #切换real_url=self.browser.current_urlif real_url in self.conf['white_list']:         #白名单continuetime.sleep(1)res_link[real_url]=self.browser.page_source     #结果获取self.browser.close()self.browser.switch_to.window(handle_this)content_md5=hashlib.md5(self.browser.page_source.encode(encoding='UTF-8')).hexdigest() #md5对比self.click_next_page(content_md5)return res_link#下一页def click_next_page(self,md5):WebDriverWait(self.browser,timeout=30,poll_frequency=1).until(EC.presence_of_element_located((By.ID, "page")))#百度搜索引擎翻页后下一页按钮 xpath 不一致 默认非第一页xpathtry:next_page_btn = self.browser.find_element_by_xpath(self.engine_conf['nextPageBtnID_xpath_s'])except:next_page_btn = self.browser.find_element_by_xpath(self.engine_conf['nextPageBtnID_xpath_f'])next_page_btn.click()#md5 进行 webpag text 对比,判断是否已翻页 (暂时使用,存在bug)i=0while md5==hashlib.md5(self.browser.page_source.encode(encoding='UTF-8')).hexdigest():#md5 对比time.sleep(0.3)#防止一些错误,暂时使用强制停止保持一些稳定i+=1if i>100:return Falsereturn True
class BrowserManage(Browser):    #打开目标搜索引擎进行搜索def search(self):self.browser.get(self.engine_conf['website'])       #打开搜索引擎站点self.send_keyword()                                 #输入搜索kwself.click_search_btn()                             #点击搜索return self.get_search_res_url()                    #获取web页搜索数据

Manage类:

from Browser import BrowserManage
from Analyse import Analyse
from FileHandle import FileHandleclass Manage:def __init__(self,conf):self.drvier=BrowserManage(conf)self.textdic=FileHandle().get_text()self.analyse=Analyse()def get_local_analyse(self):    resdic={}for k in self.textdic:res={}self.drvier.set_kw(k)url_content=self.drvier.search()#获取搜索结果及内容for k1 in url_content:res[k1]=self.analyse.get_Tfidf(self.textdic[k],url_content[k1])resdic[k]=resreturn resdic

FileHandle类:

import osclass FileHandle:#获取文件内容def get_content(self,path):f = open(path,"r")   #设置文件对象content = f.read()     #将txt文件的所有内容读入到字符串str中f.close()   #将文件关闭return content#获取文件内容def get_text(self):file_path=os.path.dirname(__file__)                                 #当前文件所在目录txt_path=file_path+r'\textsrc'                                      #txt目录rootdir=os.path.join(txt_path)                                      #目标目录内容local_text={}# 读txt 文件for (dirpath,dirnames,filenames) in os.walk(rootdir):for filename in filenames:if os.path.splitext(filename)[1]=='.txt':flag_file_path=dirpath+'\\'+filename                    #文件路径flag_file_content=self.get_content(flag_file_path) #读文件路径if flag_file_content!='':local_text[filename.replace('.txt', '')]=flag_file_content  #键值对内容return local_text

本文最终使用方法如下:

from Manage import Managewhite_list=['blog.csdn.net/A757291228','www.cnblogs.com/1-bit','blog.csdn.net/csdnnews']#白名单
#配置信息
conf={'engine':'baidu','target_page':5'white_list':white_list,}print(Manage(conf).get_local_analyse())

IT漫画、教程资料、IT文章尽在公众号碧油鸡~
在这里插入图片描述

此文转载请注明原文地址:https://blog.csdn.net/A757291228
CSDN:@1_bit

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/287852.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lru算法实现 redis_使用数组与双向链表实现一个简单的LRU算法

什么是LRU算法&#xff1f;redis大家都玩过吧&#xff0c;你们好奇redis内存数据存满之后会发生什么吗&#xff1f;抛出异常&#xff1f;禁止使用&#xff1f;还是删除数据&#xff1f;其实redis设计了一种内润淘汰机制。noeviction(默认策略)&#xff1a;屏蔽写操作&#xff0…

bzoj3224 Tyvj 1728 普通平衡树题解--Treap

题面&#xff1a; Description您需要写一种数据结构&#xff08;可参考题目标题&#xff09;&#xff0c;来维护一些数&#xff0c;其中需要提供以下操作&#xff1a; 1. 插入x数 2. 删除x数(若有多个相同的数&#xff0c;因只删除一个) 3. 查询x数的排名(若有多个相同的数&…

Blazor University (18)使用 RenderFragments 模板化组件 —— 创建 TabControl

原文链接&#xff1a;https://blazor-university.com/templating-components-with-renderfragements/creating-a-tabcontrol/创建一个 TabControl 组件源代码[1]接下来我们将创建一个 TabControl 组件。这将教您如何实现以下目标&#xff1a;将数据传递到 RenderFragment 以为其…

Java之GC机制

1 JVM基本结构 1&#xff09;类加载器classLoader&#xff1a;在JVM启动时或者类运行时将需要的.class文件加载到内存中 2&#xff09;内存区域&#xff08;运行时数据区&#xff09;&#xff1a; 是在JVM运行的时候操作所分配的内存区 3&#xff09;执行引擎&#xff1a;负…

《零基础看得懂的C语言入门教程 》——(一)脱离学习误区

本节视频连接&#xff1a; https://www.bilibili.com/video/BV1Qv411t7ae 新手C语言学习有些误区你应该知道&#xff0c;这样学习起来事半功倍~一、前言 距离上一次编写C语言的教程是5年前了&#xff08;2015年&#xff09;&#xff0c;由于自己是从初一时开始学习编程&#…

一套完整的导视设计案例_色彩导视艺术:乌克兰基辅语言学校导视设计案例

学校导视设计案例建筑师Emil Dervish为乌克兰基辅Underhub语言学校设计了色彩缤纷的导视系统&#xff0c;该设计灵感来源于伦敦地铁&#xff0c;他希望通过彩色线条的大胆应用来营造轻松而欢乐的氛围。让我们一起来看看这座由“彩虹”做导视的学校。彩虹导视设计跟着红色导视线…

C# 创建匿名管道

下面对匿名管道执行类似的操作。通过匿名管道&#xff0c;创建两个彼此通信的任务。为了给管道的创建发出信号&#xff0c;使用 ManualResetEventSlim 对象&#xff0c;与内存映射文件一样。在 Program 类的 Run 方法中&#xff0c;创建两个任务&#xff0c;调用 Reader 和 Wri…

内测投票

create table DiaoYanTiMu &#xff08;  Ids int(10) auto_increment not null primary key(),//把所需要的都写上中间不需要符号隔开&#xff0c;设自增长列类型必须是int&#xff0c;主键的话必须不能为空not null&#xff0c; Title varchar(50) not null &#xff09;;/…

Mysql 查询统计练习

2019独角兽企业重金招聘Python工程师标准>>> 1、建表 customers 顾客表 products 产品表 orders 订单表 -- 顾客表 CREATE TABLE customers (c_id INT NOT NULL AUTO_INCREMENT,lastname VARCHAR(255),firstname VARCHAR(255),address VARCHAR(255),birthday DATETI…

C++11模版元编程的应用

1.概述 关于C11模板元的基本用法和常用技巧&#xff0c;我在程序员2015年2月B《C11模版元编程》一文&#xff08;后称前文&#xff09;中已经做了详细地介绍&#xff0c;那么C11模版元编程用来解决什么实际问题呢&#xff0c;在实际工程中又该如何应用呢&#xff1f;本文将侧重…

《零基础看得懂的C语言入门教程 》——(二)C语言没那么难简单开发带你了解流程

一、学习目标 了解DevC集成开发环境了解集成开发环境了解HelloWorld程序了解HelloWorld程序的编写方法 目录 C语言真的很难吗&#xff1f;那是你没看这张图&#xff0c;化整为零轻松学习C语言。 第一篇&#xff1a;&#xff08;一&#xff09;脱离学习误区 第二篇&#xff1…

11选5下期算法_本周六周日【高二直播】辅导网课预告:通用技术电控二三极管、多用电表测量、数字逻辑电路、解析枚举递归算法,2022浙江选考技术...

01第19-21讲 2020年11月28日29日开课目录鲸学名师考点精讲系统提高高二共3阶段精品课夯实基础冲刺技术选考97-100分&#xff01;11月28日【高二|提高|直播】高二精品直播课讲授&#xff1a;浙江选考技术科目第19讲 高二综合提高鲸学名师讲授高中通用技术&#xff1a;第19讲 电控…

十分钟完成Bash 脚本进阶!列举Bash经典用法及其案例

前言&#xff1a;在linux中&#xff0c;Bash脚本是很基础的知识&#xff0c;大家可能一听脚本感觉很高大上&#xff0c;像小编当初刚开始学一样&#xff0c;感觉会写脚本的都是大神。虽然复杂的脚本是很烧脑&#xff0c;但是&#xff0c;当我们熟练的掌握了其中的用法与技巧&am…

【经典回放】多种语言系列数据结构算法:基数排序

目录 一、算法思路 二、C#语言实现 三、C语言实现 一、算法思路 1. 思想基础 基数排序的思想就是先找出待排序中的最大者&#xff0c;然后按最大者申请一个足够大的内存空间&#xff0c;并将其初始化为零&#xff0c;然后将所有待排序的数装入其中&#xff0c;标记装入的数…

探索链路追踪在.NET6工业物联网项目中的应用

如果觉得有用&#xff0c;请留言学到了。已经会了的老哥&#xff0c;请留言就这&#xff1f;可能遇到的问题工业物联网系统自上而下一般分为ERP、Mes、SCADA、WCS、边缘网关、设备等一个生产订单从SAP发送到设备要经过上述多个系统&#xff0c;当某个环节出现问题&#xff0c;可…

《零基础看得懂的C语言入门教程 》——(三)轻轻松松理解第一个C语言程序

一、学习目标 了解C语言代码的一般结构了解函数的概念了解printf函数的使用方法了解头文件的概念了解system函数的使用方法 目录 C语言真的很难吗&#xff1f;那是你没看这张图&#xff0c;化整为零轻松学习C语言。 第一篇&#xff1a;&#xff08;一&#xff09;脱离学习误…

hdu_1728_逃离迷宫(bfs)

题目连接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1728 题意&#xff1a;走迷宫&#xff0c;找最小的拐角 题解&#xff1a;对BFS有了新的理解&#xff0c;DFS剪枝应该也能过&#xff0c;用BFS就要以拐角作为增量来搜&#xff0c;即以当前点为坐标&#xff0c;4…

把文件放在SD卡

2019独角兽企业重金招聘Python工程师标准>>> 在程序中访问SDCard&#xff0c;你需要申请访问SDCard的权限。 在AndroidManifest.xml中加入访问SDCard的权限如下: <!-- 在SDCard中创建与删除文件权限--> <uses-permissionandroid:name"android.permiss…

如何用 windbg 导出 C# 中的 string 内容?

咨询区 driis我在用 windbg 调试一个生产上的 程序卡死 故障 &#xff0c;在线程栈上有一个 string 类型的参数相当大&#xff0c;我用 !dumpobj 命令不能正常显示内容&#xff0c;参考如下&#xff1a;0:036> !do 00000001b30d8668 Name: System.String MethodTable: 00000…

《零基础看得懂的C语言入门教程 》——(四)C语言的基本数据类型及变量

一、学习目标 了解C语言的基本数据类型了解变量的基本概念了解变量的使用方法了解了变量的命名方法了解格式占位符了解变量的输出 目录 C语言真的很难吗&#xff1f;那是你没看这张图&#xff0c;化整为零轻松学习C语言。 第一篇&#xff1a;&#xff08;一&#xff09;脱离…