写点什么

我一怒之下写了个抄袭举报工具!只因一觉醒来我的文章被多个平台抄袭!

用户头像
1_bit
关注
发布于: 2021 年 04 月 11 日

摘要


由于互联网的开发性,知识内容获取极其简便。某些平台、博主、机构为了个人虚荣、营销使用不光彩的手段对博主的文章进行剽窃、洗稿、抄袭,对于许多在互联网上发表技术文章、教程的博主来说是十分不公平的;我曾经听到我的一位朋友 A 说过,他在面试一位技术时,该技术展示了他的博客文章,这些文章竟然大多数是抄袭 A 的。在如今,博主对于自身劳动成果的保护意识在逐渐加深,但往往由于自身时间不够等其它因素,在对抄袭现象感到愤怒之后便不了了之。


本篇文将会编写一个小工具,随后将会逐步迭代,自动从搜索引擎中获取文章的查询结果,并且与之完成相似度对比,自动编写举报文档等,完成这一部分繁琐的过程,为保护原创增加一份力量。


工具及环境


本篇文章使用 python 作为开发语言、 selenium 作为数据抓取库、余弦相似度作为文本相似度对比方法。


使用 python 作为开发语言的原因主要有以下几点:


  • 支持库众多方便在之后的功能扩展中能够增加开发效率

  • 语法简单开发效率高


使用 selenium 作为 Web 数据抓取库的原因:


  • 使用 selenium 可以不用考虑大部分的 js 页面提高开发效率

  • 可视化的操作方式更容易错误的调试


第三方依赖库:

  • jieba

  • collections

  • selenium

  • BeautifulSoup


注:

  • 以下文章为了考虑篇幅将不赘述安装方式,尽量以简洁且完整的叙述的方式叙述如何开发完整的工具。

  • 为了使更多人能够复现本文内容,将定位本文读者为“零基础”,对部分内容将会细致的进行描述说明。


本篇文将使用颜色对不同属性的英文进行标注:

  • 本篇文代码相关

  • 名词

  • 链接


一、使用 selenium 对目标内容进行搜索


1.1 谷歌与火狐浏览器驱动安装提示


使用 selenium 还需要使用驱动对浏览器进行操作,谷歌与火狐浏览器的操作驱动并不一样,并且该驱动需要与你当前所使用的浏览器版本进行匹配,否则将出现错误,以下是两个浏览器驱动的下载地址:



以上驱动下载完毕后还需配置到系统 path 环境中,当然你可以在代码中进行路径匹配从而得到该驱动。在本文中将会默认该驱动已配置到 path 之中;配置方法在网上已经很多文章进行了解释说明,在此不再赘述。


1.2 使用火狐浏览器打开百度搜索页面


selenium 库本质上是操作驱动从而间接的对浏览器进行操作,在这里我们先新建一个 dataspider.py 文件,使用 selenium 打开一个百度搜索界面熟悉 selenium 的使用方法。


from selenium import webdriver
driver = webdriver.Firefox()url='https://www.baidu.com'driver.get(url)
复制代码


以上代码的第一行从 selenium 引入了 webdriver,之后使用 webdriver 调用 Firefox 生成一个浏览器的操作对象 driver。在此得到了 driver 后,我们可以通过 driver 调用相应的方法对浏览器进行操作。在第五行代码 driver.get(url) 调用了 get 方法,get 接收一个 url 参数,使浏览器打开一个网址,网址为 https://www.baidu.com。此时运行这一段代码,将会看到自动打开火狐浏览器并且访问百度。



1.3 完成目标内容的搜索


在 1.2 节中使用 selenium 进行了目标网址的打开,那么接下来我们将在百度搜索引擎中搜索我们原创文字的关键字,得到对应的搜索结果;在得到结果后,我们可以从搜索出来的结果中进行数据分析:对搜索结果中的文章与我们自己所写的原创文章进行相似度分析,最后再从结果中进行分类。


知道了我们的流程步骤后,我们可以分析一下正常用户在进行搜索时的操作流程。第一步打开搜索页面、第二步输入搜索关键字、第三步点击搜索键,需要模拟这个过程我们应拿到这 3 个操作所对应的 Web 页元素。在这里获取元素可以通过 driver 调用 find_element_by_id 方法,find_element_by_id 通过传入一个元素 id 作为标识,在 Web 页中获取到该元素,从而拿到该元素对象。从该搜索页中,可以得知文本框 input id kw,确认搜索按钮的 id su



此时我们已经知道了该元素的 id,那我们获取到该元素后该输入什么内容进行测试搜索呢?在此我选择 infoQ 上的 6 个月的文章热榜的榜首 《Java 失宠,谷歌宣布 Kotlin 现在是 Android 开发的首选语言》作为搜索内容,查看百度搜索出来的结果中到底有多少类似的文章:



知道了搜索内容后,我们可以通过获取到的文本框对象调用 send_keys 方法,传入搜索值即可完成在输入框中填入该值,随后获取到了确认按钮对象后,使用该对象调用 click 即可完成搜索任务,完整代码如下:


from selenium import webdriver
driver = webdriver.Firefox()url='https://www.baidu.com'driver.get(url)
textinput=driver.find_element_by_id('kw')enter=driver.find_element_by_id('su')
textinput.send_keys('Java 失宠,谷歌宣布 Kotlin 现在是 Android 开发的首选语言')enter.click()
复制代码


搜索结果如下:



从搜索结果中我们可以看见,标题有超过 1000000 个类似内容,那抄袭该文的有多少呢?由于本文所编写的工具无法进行过大的内容查询,使用 selenium 在得到了提升开发效率的同时也降低了脚本的运行效率;该工具所面向的用户是个人博主,在某些文章被抄袭时可简单的完成抄袭内容的记录。


此时我们得到了该数据后,接下来就应该对搜索出来的数据进行收集、记录到本地,方便接下来的相似度分析。


1.4 对搜索结果进行记录


我们需要对搜索结果进行记录需要使用 driver 调用 page_source 属性获取整个 Web 页的 HTML 内容,随后对整个 HTML 内容进行解析。对 HTML 进行解析在这里使用一个 HTML 解析工具 BeautifulSoupBeautifulSoup 提供了多种方法对解析后的 HTML 文本进行指定内容的提取。


首先我们在前几节所新建的 python 文件 dataspider.py 头部追加引入 BeautifulSoup


from bs4 import BeautifulSoup
复制代码


dataspider.py 文件的代码尾部追加使用 BeautifulSoup 解析 HTML 内容的代码:


#获取页面源码html=driver.page_source 
#使用 BeautifulSoup 新建解析 html 内容,指定解析器为 html.parsersoup = BeautifulSoup(html, "html.parser") 
复制代码


随后我们查看搜索结果的 Web 页,发现搜索出来的结果都在 class t h3 标签之中,并且对应的 a 标签也存在于 h3 元素之内。



这时我们可以通过使用 的 select 方法对一般指定标记了对应 css 样式的元素对象进行获取。


search_res=soup.select('.t')
复制代码


此时 search_res 将会得到一个内容为 h3 元素的 html 内容列表。显然,我们想要获取的内容并不是 html,而是对应的 title 文本以及对应的超链接地址。此时我们可以使用遍历取出对应的 a 元素,随后再取 a 元素的 href 属性内容:


#遍历结果取值for res in search_res:    print(res.a['href'])
复制代码


这个时候运行代码可能会取不到值,原因是我们并未等待浏览器解析就直接获取了页面数据,此时可以使用循环对获取的搜索结果进行判断,若为空则继续获取:


for i in range(1000):    #获取页面源码    html=driver.page_source    #使用 BeautifulSoup 新建解析 html 内容,指定解析器为 html.parser    soup=BeautifulSoup(html,"html.parser")    search_res=soup.select('.t')    if len(search_res)>0:        break
#遍历结果取值for res in search_res: print(res.a['href'])
复制代码


我使用的是 vscode,运行脚本后在终端中将会出现对应的超链接内容:



1.5 获取结果的真实链接


此时我们得到的结果并不是搜索出的资源的真实地址,需要进行过滤后提取。



此时只需要访问其地址,得到正确的 url 后关掉其页面即可。


第一步使用 execute_script 执行 js 代码新开一个 Web 页:


for res in search_res:    jscode = 'window.open("'+res.a['href']+'")'    driver.execute_script(jscode)
复制代码


此时将会打开一个 Web 页,我们需要对 driver 获取操作的浏览器页面对象,须使用 driver 调用 current_window_handle 获取当前句柄作为保留,以便于之后的句柄切换:


handle_this=driver.current_window_handle # 保留当前句柄
复制代码


随后获取所有的可操作句柄:


handle_all=driver.window_handles # 获取所有句柄
复制代码


之后就简单的使用 for 对所有的句柄进行遍历,如果不是等于当前的操作页句柄就将其赋予给操作句柄变量即可,此时就可以完成切换 Web 页操作对象:


handle_exchange=None #要操作的句柄for handle in handle_all:   if handle != handle_this: #非当前句柄就交换        handle_exchange = handle
复制代码


得到句柄之后使用 driver 调用 switch_to 方法传入所需切换操作的 Web 页句柄:


driver.switch_to.window(handle_exchange)#切换
复制代码


此时已经完成了句柄的切换操作,那么直接使用 driver 调用 current_url 属性即可得到一个真实的目标 url 地址,并且将其存储到一个列表中进行保存即可:


real_url=driver.current_url
复制代码


接着使用 close 方法关闭当前网页,并且再次使用 switch 方法将句柄切换至原有操作页句柄即可:


driver.close()driver.switch_to.window(handle_this)
复制代码


其实此时获取真实链接地址的代码并不严谨,因为在进行请求时,得到的真实链接将会有一定的重定向时间,否则无法获取到相应的真实地址,值为 about:blank。解决这个问题可以对当前的浏览器页面 url 值进行判断,若为 about:blank 将继续循环获取该页面值,并且为了防止意外 bug,在超过 100 次循环时则跳出当前循环,并且赋值真实地址为 null,最后将真实地址存入一列表中,修改代码后该部分的完整代码如下:


link_res=[]#遍历结果取值for res in search_res:    jscode = 'window.open("'+res.a['href']+'")'    driver.execute_script(jscode)    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 = handle driver.switch_to.window(handle_exchange) real_url=driver.current_url if real_url=='about:blank': i=0 while True: real_url=driver.current_url if real_url!='about:blank': break if i>10000: real_url='null' break i+=1 driver.close() driver.switch_to.window(handle_this) link_res.append(real_url) print(real_url)
复制代码


终端输出结果如下:



1.6 修改 dataspider.py 代码结构


为了方便之后进行扩展,我们对已有的代码结构进行一下优化。


新建一个类名叫 Spider,创建一个 open_engine 方法用于打开浏览器以及搜索引擎页;为这一步编写方法的原因是查重不一定是一个搜索引擎,可能会使用不同的搜索引擎,并且每个搜索引擎的策略可能不一:


#打开引擎class Spider():  def open_engine(self,driver,engine):    if engine=='baidu':      url='https://www.baidu.com'      driver.get(url)      self.input_id='kw'      self.click_id='su'
复制代码


该方法接收两个参数,分别是 driver 以及 enginedriver 为浏览器对象,engine 为搜索引擎;在方法中指定搜索页链接,随后设定当前搜索框 id 以及搜索按钮的 id


接下来创建一个 enter_kw 方法用于键入关键字并且进行搜索:


#键入关键字def enter_kw(self,driver):  textinput=driver.find_element_by_id(self.input_id)  enter=driver.find_element_by_id(self.click_id)  textinput.send_keys('Java 失宠,谷歌宣布 Kotlin 现在是 Android 开发的首选语言')  enter.click()
复制代码


该方法传入了浏览器操作对象 driver,具体代码已有说明不再赘述。


之后新建一个 get_search_link 方法用于分析搜索引擎的结果获取:


#搜索结果链接def get_search_link(self,driver,engine):  self.open_engine(driver,engine)  self.enter_kw(driver)
engine_res=[] for i in range(10000): engine_res=[] #获取页面源码 html=driver.page_source #使用 BeautifulSoup 新建解析 html 内容,指定解析器为 html.parser soup=BeautifulSoup(html,"html.parser") engine_res=soup.select('.t') if len(engine_res)>0: break return engine_res
复制代码


在这个方法中,使用了 open_engine 方法以及 enter_kw 方法。在该方法中使用 for 循环等待页面内容获取,若浏览器加载了该页面,那么就可以获取到 搜索内容,engine_res 列表长度则大于 0,即跳出循环。


最后创建 get_real_link 方法用于获取真实连接:


    #获取 real link    def get_real_link(self,engine_res,driver):        #遍历结果取值        real_res=[]        for res in engine_res:            jscode='window.open("'+res.a['href']+'")'            driver.execute_script(jscode)            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=handle driver.switch_to.window(handle_exchange) real_url=driver.current_url if real_url=='about:blank': i=0 while True: real_url=driver.current_url if real_url!='about:blank': break if i>10000: real_url='null' break i+=1 driver.close() driver.switch_to.window(handle_this) real_res.append(real_url) #print(real_url) return real_res
复制代码


最后编写一个 get_search_res 方法用于调用,完整的 Spider 类如下:


class Spider():        def get_search_res(self,driver,engine):        engine_res=self.get_search_link(driver,engine)        return self.get_real_link(engine_res,driver)        #打开引擎    def open_engine(self,driver,engine):        if engine=='baidu':            url='https://www.baidu.com'            driver.get(url)            self.input_id='kw'            self.click_id='su'        #键入关键字    def enter_kw(self,driver):        textinput=driver.find_element_by_id(self.input_id)        enter=driver.find_element_by_id(self.click_id)        textinput.send_keys('Java 失宠,谷歌宣布 Kotlin 现在是 Android 开发的首选语言')        enter.click()
#搜索结果链接 def get_search_link(self,driver,engine): self.open_engine(driver,engine) self.enter_kw(driver)
engine_res=[] for i in range(10000): engine_res=[] #获取页面源码 html=driver.page_source #使用 BeautifulSoup 新建解析 html 内容,指定解析器为 html.parser soup=BeautifulSoup(html,"html.parser") engine_res=soup.select('.t') if len(engine_res)>0: break return engine_res
#获取 real link def get_real_link(self,engine_res,driver): #遍历结果取值 real_res=[] for res in engine_res: jscode='window.open("'+res.a['href']+'")' driver.execute_script(jscode) 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=handle driver.switch_to.window(handle_exchange) real_url=driver.current_url if real_url=='about:blank': i=0 while True: real_url=driver.current_url if real_url!='about:blank': break if i>10000: real_url='null' break i+=1 driver.close() driver.switch_to.window(handle_this) real_res.append(real_url) #print(real_url) return real_res
复制代码


接下来直接新建 Spider 类即可完成页面数据抓取:


s=Spider()driver=webdriver.Firefox()res=s.get_search_res(driver,'baidu')print(res)
复制代码


二、使用余弦相似度对数据进行对比


2.1 余弦相似度(参考阮一峰)


余弦相似度是一种较为简单的文本相似度对比方法,首先需要进行分词,其次进行词向量计算,最后进行相似度分析。分词我们需要使用 jieba 中文分词库,并且使用 collections 进行次数统计。


余弦相似度使用在文本相似度分析,主要是将句子中的词进行切词,随后去除部分停词后计算剩余词语的词频,随后计算向量;该词频向量是指单一个词语所出现的次数,这个时候文本相似度就变成计算这两个向量之间的相似度。



将两个向量当作这两条线段,我们默认指向不同的方向,若两条线段越接近,或者说形成的夹角越小,那么则可以说明这两个向量之间约接近,或者说是这两个文本之间相似度越近。那么可以得出夹角θ:



例如有两个句子:

  • 1:我在 infoQ 写作很棒,我很开心,我在这里能够认识很多朋友。

  • 2:我在 infoQ 写文章认识很多朋友,例如小明、小红都是在这里认识的,我很开心,感觉很棒。


我们将句子 1 可以分词为:我、infoQ 、写、很棒、开心、认识、很多、朋友。

我们将句子 2 可以分词为:我、infoQ 、写、文章、认识、朋友、小明、小红、开心、很棒。


之后将词频进行统计:

1:[我:3,infoQ :1,写:1,很棒:1,开心:1,认识:1,很多:1,朋友:1]

2:[我:2,infoQ :1,写:1,文章:1,认识:2,朋友:1,开心:1,很棒:1]


转换为:


1: [2,1,1,1,1,1,1,1]

2: [2,1,1,1,2,1,1,1]


最后套入公式之中为:


(2*2+1*1+1*1+1*1+1*2+1*1+1*1+1*1)/sqrt(sqr(2)+7)*sqrt(sqr(2)+sqr(2)+6)


最终结果如下:



如果给予一个百分比则为是 96% 相似度。


2.2 实现相似度计算


在这一部分中,我们需要新建一个 python 文件名为 Analyse.py,并且引入 jieba 库与 collectionsjieba 用于中文分词,collections 用于统计次数。此时在 Analyse 类中添加一个方法用于提取文本中权重的词语:


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


这一步其实就是分词,并且返回权重值 Top 20 的词语,随后使用 Counter 进行计数,方便之后使用。接着添加一个词袋合并,因为我们是两个文本作为对比,我们将其权重 Top 20 的词语进行合并,弱两者极为接近,那么最后在通过合并后的词袋进行向量计算时结果将会相近:


#词合并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
复制代码


T1T2 是两个文本中权重 Top 20 的词语,随后合并到 MergeWord 列表中。


接着开始计算向量:


#得出文档向量def CalVector(self,T1,MergeWord):    TF1 = [0] * len(MergeWord)    for ch in T1:        TermFrequence = T1[ch]        word = ch        if word in MergeWord:            TF1[MergeWord.index(word)] = TermFrequence    return TF1
复制代码


接着我们添加一个方法用于计算相似度(以上以说过如何进行计算,在此就不再赘述):


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


最后,我们添加一个方法用于调用这些计算步骤方便使用


def get_Tfidf(self,text1,text2):        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 lcutimport jieba.analyseimport collections
class 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 = ch if word in MergeWord: TF1[MergeWord.index(word)] = TermFrequence return TF1 #计算 TF-IDF def cosine_similarity(self,vector1, vector2): dot_product = 0.0 normA = 0.0 normB = 0.0
for a, b in zip(vector1, vector2):#两个向量组合成 [(1, 4), (2, 5), (3, 6)] 最短形式表现 dot_product += a * b normA += a ** 2 normB += b ** 2 if normA == 0.0 or normB == 0.0: return 0 else: return round(dot_product / ((normA**0.5)*(normB**0.5))*100, 2)
复制代码


2.3 本地文本数据获取


此时已编写相似度对比类、浏览器搜索结果获取,接下来我们可以用一种较为简单的方式实现搜索结果的文本对比。


InfoQ 中将原文文本复制到当前目录下的 txtsrc 目录(新建目录)保存为 txt 文件,在此为了方便查重使用标题命名为“ Java 失宠,谷歌宣布 Kotlin 现在是 Android 开发的首选语言”:



dataspider.py 中编写一个类为 Srcdata 用来获取需要查重的源数据,在该类中添加两个方法,一个用于批量获取文本名,一个用于获取需要查重文章的文本数据:


class Srcdata():  srcroot=os.getcwd()+r'/txtsrc/'  def getlocaltxt(self):      name=self.gettxtfile()      txt={}      for p in name:          f = open(self.srcroot+p,'r')          txt[p]=f.read()          f.close()      return txt
def gettxtfile(self): os.chdir(self.srcroot) # 切换到指定目录 name=[] for filename in glob.glob("*.txt"): name.append(filename) return name
复制代码


以上代码中 srcroot 为指定的文本查重根目录,gettxtfile 方法用于获取 txt 文件名,getlocaltxt 方法用于获取文本数据(此处需要 import os,glob)。


在此获取文本数据的原因是可以准确的消除对比数据的“噪点”,因为在接下的文本相似度对比中,所取的文本数据为 Web 页面数据,会有一部分内容干扰。在此为了最大限度保证准确性,源数据保持了准确度。


不过此方面也可以保证质量,使用 selenium 通过 xpath 可以方便且准确的获取 InfoQ 线上 Web 的文本数据,也可以从搜索结果中挑选出准确的文本数据,但是由于考虑篇幅,将会在之后的迭代版本中实现。


2.4 进行文本相似度对比


此时对于搜索结果的文本,我们可以在获取真实连接后强制一段时间等待浏览器加载数据,随后获取整页数据,并且将原有 real_res 列表改为字典,存储对应链接与文本数据的键值对。在 Spider 类的 get_real_link 方法中修改,如下代码:


def get_real_link(self,engine_res,driver):        #遍历结果取值        real_res={}#更改为字典(新增)        for res in engine_res:            jscode='window.open("'+res.a['href']+'")'            driver.execute_script(jscode)            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=handle driver.switch_to.window(handle_exchange) real_url=driver.current_url if real_url=='about:blank': i=0 while True: real_url=driver.current_url if real_url!='about:blank': break if i>10000: real_url='null' break i+=1 time.sleep(5)#强制等待加载(新增) html_txt=driver.page_source#获取网页数据(新增) driver.close() driver.switch_to.window(handle_this) real_res[real_url]=html_txt#存储键值对(新增) #print(real_url) return real_res
复制代码


接下来为了方便测试,直接在 dataspider.py 文件下重新编写调用代码,首先获取本地的文本数据、接着新建浏览器操作对象、创建 Spider 对象以及文本相似度分析对象(需要 from Analyse import Analyse):


src=Srcdata().getlocaltxt()#获取本地文本driver=webdriver.Firefox()#火狐浏览器s=Spider()#抓取搜索结果及 web 数据als=Analyse()#相似度分析
复制代码


接着创建一个 res_html 列表保存数据,遍历本地文本内容使用文件名作为 关键字 kw 搜索,传入 get_search_res 方法中;get_search_res 已做修改,之后将贴出所有代码以供复现。在传入关键字搜索后,遍历搜索结果,使用 Unicode 提取汉字,最终添加到 res_html 列表中进行保存(正则 import re ):


res_html=[]for kw in src:    res=s.get_search_res(driver,'baidu',kw)        for k in res:        clean_str = ''.join(re.findall('[\u4e00-\u9fa5]',res[k]))#使用 Unicode 提取汉字        src_str = ''.join(re.findall('[\u4e00-\u9fa5]',src[kw]))#使用 Unicode 提出汉字        #print(k,'相似度:',als.get_Tfidf(src_str,clean_str))        res_html.append([k,kw,als.get_Tfidf(src_str,clean_str)])
复制代码


此时编写一个类,生成 html 文件保存结果(基础代码不再赘述):


class Analyseres():    def res(self,res_html):        html_str=''        for v in res_html:            html_str+='<h4><a href="'+str(v[0])+'">'+str(v[1])+'</a> 相似度:'+str(v[2])+'</h4>'        self.writefile(html_str)    def writefile(self,html):        filename = './res.html'        with open(filename, 'w') as file_object:            file_object.write(html)            print('save res done')
复制代码


并且在结尾处保存结果:


wres=Analyseres()wres.res(res_html)
复制代码


运行代码后终端将出现如下结果:



将会生成 res 命名的 html 文件:



2.5 结果分析


我们此时可以通过结果查看低相似度的文章结果是否正确:


在此列出一篇文章作为例子,可以看出两篇文章内容相似度不高,相似度结果可信度比较高:



再此再使用一篇较高相似度的文章进行对比,结果发现文章重复度较高,由此可见该工具对查重有一定的准确度:



三、总结


3.1 改进及优化


以下功能将会在之后的文章中进行迭代:


  • 此工具在本地数据获取并不方便,可以编写方法进行扩展,直接获取博主的文章列表,通过 xpath 进行文章数据获取。

  • 在进行 Web 搜索时翻页并未实现,也通过 id 或者 class 获取到元素,随后进行点击翻页。

  • 浏览器查重应考虑多个搜索引擎,通过不同策略完成搜索引擎的配置,从而实现不同搜索引擎的查重,包括微信公众号内容的查重搜索。

  • 在过滤数据时可考虑部分头部平台,在头部平台中的网站可设置策略,自动精确获取文本数据。

  • 白名单设置,部分网站不需要抓取,否则将影响查重数据。


3.2 完整代码


dataspider.py


from selenium import webdriverfrom bs4 import BeautifulSoupfrom selenium.webdriver.support.ui import WebDriverWaitimport glob,os,time,refrom Analyse import Analyse
class Spider(): def get_search_res(self,driver,engine,kw): engine_res=self.get_search_link(driver,engine,kw) return self.get_real_link(engine_res,driver) #打开引擎 def open_engine(self,driver,engine): if engine=='baidu': url='https://www.baidu.com' driver.get(url) self.input_id='kw' self.click_id='su' #键入关键字 def enter_kw(self,driver,kw): textinput=driver.find_element_by_id(self.input_id) enter=driver.find_element_by_id(self.click_id) textinput.send_keys(kw) enter.click()
#搜索结果链接 def get_search_link(self,driver,engine,kw): self.open_engine(driver,engine) self.enter_kw(driver,kw)
engine_res=[] for i in range(10000): engine_res=[] #获取页面源码 html=driver.page_source #使用 BeautifulSoup 新建解析 html 内容,指定解析器为 html.parser soup=BeautifulSoup(html,"html.parser") engine_res=soup.select('.t') if len(engine_res)>0: break return engine_res
#获取 real link def get_real_link(self,engine_res,driver): #遍历结果取值 real_res={} for res in engine_res: jscode='window.open("'+res.a['href']+'")' driver.execute_script(jscode) 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=handle driver.switch_to.window(handle_exchange) real_url=driver.current_url if real_url=='about:blank': i=0 while True: real_url=driver.current_url if real_url!='about:blank': break if i>10000: real_url='null' break i+=1 time.sleep(5) html_txt=driver.page_source driver.close() driver.switch_to.window(handle_this) real_res[real_url]=html_txt #print(real_url) return real_res
class Srcdata(): srcroot=os.getcwd()+r'/txtsrc/' def getlocaltxt(self): name=self.gettxtfile() txt={} for p in name: f = open(self.srcroot+p,'r') txt[p]=f.read() f.close() return txt
def gettxtfile(self): os.chdir(self.srcroot) # 切换到指定目录 name=[] for filename in glob.glob("*.txt"): name.append(filename) return name
class Analyseres(): def res(self,res_html): html_str='' for v in res_html: html_str+='<h4><a href="'+str(v[0])+'">'+str(v[1])+'</a> 相似度:'+str(v[2])+'</h4>' self.writefile(html_str) def writefile(self,html): filename = './res.html' with open(filename, 'w') as file_object: file_object.write(html) print('save res done')
src=Srcdata().getlocaltxt()driver=webdriver.Firefox()s=Spider()als=Analyse()
res_html=[]for kw in src: res=s.get_search_res(driver,'baidu',kw) for k in res: clean_str = ''.join(re.findall('[\u4e00-\u9fa5]',res[k])) src_str = ''.join(re.findall('[\u4e00-\u9fa5]',src[kw])) #print(k,'相似度:',als.get_Tfidf(src_str,clean_str)) res_html.append([k,kw,als.get_Tfidf(src_str,clean_str)])wres=Analyseres()wres.res(res_html)
复制代码


Analyse.py


from jieba import lcutimport jieba.analyseimport collections
class 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 = ch if word in MergeWord: TF1[MergeWord.index(word)] = TermFrequence return TF1 #计算 TF-IDF def cosine_similarity(self,vector1, vector2): dot_product = 0.0 normA = 0.0 normB = 0.0
for a, b in zip(vector1, vector2):#两个向量组合成 [(1, 4), (2, 5), (3, 6)] 最短形式表现 dot_product += a * b normA += a ** 2 normB += b ** 2 if normA == 0.0 or normB == 0.0: return 0 else: return round(dot_product / ((normA**0.5)*(normB**0.5))*100, 2)
复制代码


发布于: 2021 年 04 月 11 日阅读数: 458
用户头像

1_bit

关注

还未添加个人签名 2021.02.05 加入

CSDN博客之星TOP5 CSDN博客专家 CSDN原力计划合作者 蓝桥云课签约作者

评论 (55 条评论)

发布
用户头像
厉害(小白的崇拜)
2021 年 04 月 15 日 09:25
回复
用户头像
厉害,(来自小白的仰视)
2021 年 04 月 15 日 09:25
回复
用户头像
大佬带带我
2021 年 04 月 12 日 16:35
回复
哈哈哈,我的数据已经停歇不前
2021 年 04 月 12 日 20:28
回复
用户头像
一个字,牛批plus!
2021 年 04 月 12 日 14:21
回复
多谢大佬
2021 年 04 月 12 日 14:27
回复
用户头像
bit姐yyds
2021 年 04 月 12 日 14:18
回复
yysd
2021 年 04 月 12 日 14:18
回复
用户头像
兰清姐姐加油╮( ̄▽ ̄)╭
2021 年 04 月 11 日 22:40
回复
你这个helloword有意思
2021 年 04 月 11 日 23:29
回复
用户头像
yyds
2021 年 04 月 11 日 18:08
回复
yyds啥意思
2021 年 04 月 11 日 23:29
回复
用户头像
厉害!点赞支持!
2021 年 04 月 11 日 13:03
回复
欢迎袁哥😊
2021 年 04 月 11 日 13:19
回复
用户头像
冲冲冲
2021 年 04 月 11 日 12:49
回复
加油就对了
2021 年 04 月 11 日 12:54
回复
用户头像
冲冲冲冲
2021 年 04 月 11 日 12:35
回复
冲冲冲
2021 年 04 月 11 日 12:43
回复
用户头像
好文,很多文章被公众号不问自取,是时候查一查了
2021 年 04 月 11 日 12:28
回复
后面直接做好策略对搜狗搜索就可以找公众号文章
2021 年 04 月 11 日 12:43
回复
用户头像
yyds,爱你大佬
2021 年 04 月 11 日 12:26
回复
[社会社会][社会社会][社会社会]
2021 年 04 月 11 日 12:43
回复
用户头像
bit 哥牛逼牛逼牛逼
2021 年 04 月 11 日 12:23
回复
牛逼冲冲冲
2021 年 04 月 11 日 12:43
回复
用户头像
敢抄bit哥多少有点不知好歹哈哈哈哈!
Bit哥肝文肝到爆!持续原动力输出哈哈
“什么是快乐星球🐮 ”
2021 年 04 月 11 日 12:05
回复
生产队的驴,冲冲冲
2021 年 04 月 11 日 12:06
回复
用户头像
写得很好
2021 年 04 月 11 日 12:01
回复
非常感谢支持
2021 年 04 月 11 日 12:02
回复
用户头像
支持bit哥
2021 年 04 月 11 日 11:58
回复
非常感谢支持~
2021 年 04 月 11 日 11:58
回复
用户头像
点赞
2021 年 04 月 11 日 11:52
回复
多谢支持
2021 年 04 月 11 日 11:53
回复
用户头像
敢抄bit哥! 多少有点看不清楚局势呀!bit_yyds
2021 年 04 月 11 日 11:52
回复
[捂脸]哈哈哈,确实这篇文抄袭的比较低
2021 年 04 月 11 日 11:54
回复
用户头像
牛逼格拉斯
2021 年 04 月 11 日 11:52
回复
666流批就好
2021 年 04 月 11 日 11:54
回复
用户头像
大佬大佬
2021 年 04 月 11 日 11:52
回复
小工具
2021 年 04 月 11 日 11:54
回复
加载更多
我一怒之下写了个抄袭举报工具!只因一觉醒来我的文章被多个平台抄袭!