查重

Posted 1_bit

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了查重相关的知识,希望对你有一定的参考价值。

前言

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

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

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

技术

本文实现的查重功能为了考虑适配大多数站点,从而使用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 webdriver

url='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 webdriver

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()

浏览器自动完成了键入搜索关键字及搜索功能:

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 time

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')
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 time

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 = handle
    driver.switch_to.window(handle_exchange)#切换
    real_url=driver.current_url
    print(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 = 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):#测试对比本地数据对比搜索引擎方法
        # 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 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)
     

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 Analyse

def 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 = handle
    driver.switch_to.window(handle_exchange)#切换
    real_url=driver.current_url
    
    time.sleep(5)
    html_2=driver.page_source
    print('相似度:',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=conf
		self.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_elem

以上是关于查重的主要内容,如果未能解决你的问题,请参考以下文章

文本查重算法SimHash

JQuery Validate插件如何自定义验证方法(结合ajax实现数据库的查重)

如何进行论文查重

论文查重间隔多久时间重复率会不一样呢?

论文查重

python代码查重原理