乐趣区

关于python: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 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_element_located((By.ID, "page")))
        #内容通过 BeautifulSoup 解析
        content=self.browser.page_source
        soup = 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 = handle
            self.browser.switch_to.window(handle_exchange)  #切换
            real_url=self.browser.current_url
            
            time.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()                                 #输出搜寻 kw
        self.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 time

class Browser:
    def __init__(self,conf):
        self.browser=webdriver.Chrome()
        self.conf=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'])
    #搜寻框点击
    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_source
        soup = 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 = handle
            self.browser.switch_to.window(handle_exchange)  #切换
            real_url=self.browser.current_url
            
            time.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()                                 #输出搜寻 kw
        self.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.engineConf

class 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):
        pass


class 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 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',
    }
    
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 os

class 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()                                 #输出搜寻 kw
        self.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 不统一 默认非第一页 xpath
        try:
            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=0
        while md5==hashlib.md5(self.browser.page_source.encode(encoding='UTF-8')).hexdigest():#md5 比照
            time.sleep(0.3)# 避免一些谬误,临时应用强制进行放弃一些稳固
            i+=1
            if i>100:
                return False
        return 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_source
        soup = 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 = handle
                self.browser.switch_to.window(handle_exchange)  #切换
                real_url=self.browser.current_url
                if real_url in self.conf['white_list']:         #白名单
                    continue
                time.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 FileHandle

class 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]=res
        return resdic

以上代码初始化办法接管一个参数,且初始化办法中新建了 BrowserManage 对象、Analyse对象以及获取了文本内容。
get_local_analyse办法遍历文本,应用文件名当作关键字进行搜寻,并且将搜寻内容与以后文本做类似度比照,最初返回后果。
后果如下:

博主目录下文件如下:

类似度剖析局部以上为次要内容,工具之后将会丢 GitHubcsdn的代码仓库中,应用的无头模式,本篇所讲的内容为个别实现。

所有残缺的代码如下

Analyse 类:

from jieba import lcut
import jieba.analyse
import collections
from FileHandle import FileHandle

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)

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 xlwt

class Browser:
    def __init__(self,conf):
        self.browser=webdriver.Chrome()
        self.conf=conf
        self.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_source
        soup = 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 = handle
                self.browser.switch_to.window(handle_exchange)  #切换
                real_url=self.browser.current_url
                if real_url in self.conf['white_list']:         #白名单
                    continue
                time.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 不统一 默认非第一页 xpath
        try:
            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=0
        while md5==hashlib.md5(self.browser.page_source.encode(encoding='UTF-8')).hexdigest():#md5 比照
            time.sleep(0.3)# 避免一些谬误,临时应用强制进行放弃一些稳固
            i+=1
            if i>100:
                return False
        return True
class BrowserManage(Browser):    
    #关上指标搜索引擎进行搜寻
    def search(self):
        self.browser.get(self.engine_conf['website'])       #关上搜索引擎站点
        self.send_keyword()                                 #输出搜寻 kw
        self.click_search_btn()                             #点击搜寻
        return self.get_search_res_url()                    #获取 web 页搜寻数据

Manage 类:

from Browser import BrowserManage
from Analyse import Analyse
from FileHandle import FileHandle

class 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]=res
        return resdic

FileHandle 类:

import os

class 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 Manage

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

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

退出移动版