CSDN文章被洗稿、抄襲嚴重!用Python作一個「基於搜索引擎的文章查重工具」,解決!


做者:  1_bit
原文連接:http://suo.im/5V1JpX

前言

文章抄襲在互聯網中廣泛存在,不少博主、號主深受其煩。 近幾年隨着互聯網的發展,抄襲等不道德行爲在互聯網上愈演愈烈,甚至複製、黏貼後發佈標原創家常便飯,部分抄襲後的文章,甚至標記了一些聯繫方式從而使讀者獲取源碼等資料,這種惡劣的行爲令人憤慨。php

本文使用搜索引擎結果做爲文章庫,再與本地或互聯網上數據作類似度對比,實現文章查重;因爲查重的實現過程與通常狀況下的微博情感分析實現流程類似,從而輕易的擴展出情感分析功能(下一篇將在此篇代碼的基礎上完成數據採集、清洗到情感分析的整個過程)。html

因爲近期時間上並不充裕,暫時實現了主要功能,細節上並無進行優化,可是在代碼結構上進行了一些簡要的設計,使得以後的功能擴展、升級更爲簡便。我本人也將會持續更新該工具的功能,爭取讓這個工具在技術上更加的成熟、實用。python

技術

本文實現的查重功能爲了考慮適配大多數站點,從而使用selenium用做數據獲取,配置不一樣搜索引擎的信息,實現較爲通用的搜索引擎查詢,而且不須要考慮過多的動態數據抓取;分詞主要使用jieba庫,完成對中文語句的分詞;使用餘弦類似度完成文本類似度的對比,並導出對比數據至Excel文章留做舉報信息。web

微博情感分析基於sklearn,使用樸素貝葉斯完成對數據的情感分析;在數據抓取上,實現流程與文本查重的功能相似。算法

測試代碼獲取

CSDN codechina 代碼http://suo.im/6wCLEichrome

環境

做者的環境說明以下:編程

  • 操做系統:Windows7 SP1 64
  • python 版本:3.7.7
  • 瀏覽器:谷歌瀏覽器
  • 瀏覽器版本:80.0.3987 (64 位)

若有錯誤歡迎指出,歡迎留言交流。windows

1 實現文本查重

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))*1002)

類似度方法接收兩個向量,隨後計算類似度並返回。爲了代碼冗餘度少,在這裏先簡單的添加一個方法,完成計算流程:

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))*1002)
     

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)

運行腳本:

結果顯示有幾個高度類似的連接,那麼這幾個就是疑似抄襲的文章了。以上是完成基本查重的代碼,可是相對於說代碼比較冗餘、雜亂,接下來咱們優化一下代碼。

2 代碼優化

經過以上的程序編程,簡要步驟能夠分爲:獲取搜索內容->獲取結果->計算類似度。咱們能夠新建三個類,分別爲:Browser、Analyse(已新建)、SearchEngine。Browser用於搜索、數據獲取等;Analyse用於類似度分析、向量計算等;SearchEngine用於不一樣搜索引擎的基本配置,由於大部分搜多引擎的搜索方式較爲一致。

2.1 Browser 類

初始化新建一個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.2 SearchEngine 類

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 功能擴展

暫時這個小工具的功能只有查重這個基礎功能,而且這個存在不少問題。如沒有白名單過濾、只能查一篇文章的類似度、若是比較懶也沒有直接獲取文章列表自動查重的功能以及結果導出等。接下來慢慢完善部分功能,因爲篇幅關係並不徹底把的功能實如今此列出,以後將會持續更新。

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.2 BrowserManage類

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.3 Browser類的擴展

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.4 get_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))*1002)

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


福利時間

噹噹雙十一大促,爲你們謀了點福利

每滿100減50

滿200減100

滿300減150

……

滿400減200的同時,會附贈40元的疊加券


至關於全場打 4折!

使用方法:複製 40元 專屬疊加優惠碼:XZSJZF,記得使用優惠碼,手慢無!

  • 步驟一:複製指定優惠碼

  • 步驟二:長摁二維碼,直達專題,挑選圖書至購物車,點擊結算

               

  • 步驟三:點擊「優惠券/碼」處,輸入優惠碼  XZSJZF(注意要大寫)

                 


使用條件:購書實際支付金額滿200,便可享受再減40的優惠。

使用範圍:全場噹噹自營圖書!

在這裏也推薦幾本好書給你們:

《Python深度學習》

本書由Keras之父、現任Google人工智能研究員的弗朗索瓦•肖萊(François Chollet)執筆,詳盡介紹了用Python和Keras進行深度學習的探索實踐,包括計算機視覺、天然語言處理、產生式模型等應用。書中包含30多個代碼示例,步驟講解詳細透徹。因爲本書立足於人工智能的可達性和大衆化,讀者無須具有機器學習相關背景知識便可展開閱讀。在學習完本書後,讀者將具有搭建本身的深度學習環境、創建圖像識別模型、生成圖像和文字等能力。


《Python數據分析:活動Pandas》

輕鬆掌握流行的Python數據分析工具

深刻淺出,示例豐富,容易理解和上手


本書是Python數據分析入門書,每一個概念都經過簡單實例來闡述,便於讀者理解與上手。具體內容包括:Python及Pandas基礎知識,加載和查看數據集,Pandas的DataFrame對象和Series對象,使用matplotlib、seaborn和Pandas提供的繪圖方法爲探索性數據分析做圖,鏈接與合併數據集,處理缺失數據,清理數據,轉換數據類型,處理字符串,應用函數,分組操做,擬合及評估模型,正則化方法與聚類技術等。



END



用Python分析了近幾年胡潤排行榜,我酸了...


爬蟲|如何在scrapy請求異常以後再設置代理IP


拜登當選,Python之父大喊Yes!吳恩達:讓我鬆了口氣!


本文分享自微信公衆號 - Python編程與實戰(GoPy1024)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索