1. 程式人生 > >python 手把手教你基於搜尋引擎實現文章查重

python 手把手教你基於搜尋引擎實現文章查重

# 前言 文章抄襲在網際網路中普遍存在,很多博主都收受其煩。近幾年隨著網際網路的發展,抄襲等不道德行為在網際網路上愈演愈烈,甚至複製、黏貼後釋出標原創屢見不鮮,部分抄襲後的文章甚至標記了一些聯絡方式從而使讀者獲取原始碼等資料。這種惡劣的行為使人憤慨。 本文使用搜索引擎結果作為文章庫,再與本地或網際網路上資料做相似度對比,實現文章查重;由於查重的實現過程與一般情況下的微博情感分析實現流程相似,從而輕易的擴展出情感分析功能(下一篇將在此篇程式碼的基礎上完成資料採集、清洗到情感分析的整個過程)。 由於近期時間上並不充裕,暫時實現了主要功能,細節上並沒有進行優化,但是在程式碼結構上進行了一些簡要的設計,使得之後的功能擴充套件、升級更為簡便。我本人也將會持續更新該工具的功能,爭取讓這個工具在技術上更加的成熟、實用。 # 技術 本文實現的查重功能為了考慮適配大多數站點,從而使用**selenium**用作資料獲取,配置不同搜尋引擎的資訊,實現較為通用的搜尋引擎查詢,並且不需要考慮過多的動態資料抓取;分詞主要使用**jieba**庫,完成對中文語句的分詞;使用餘弦相似度完成文字相似度的對比並匯出對比資料至**Excel**文章留作舉報資訊。 微博情感分析基於**sklearn**,使用樸素貝葉斯完成對資料的情感分析;在資料抓取上,實現流程與文字查重的功能類似。 # 測試程式碼獲取 CSDN codechina 程式碼倉庫:[https://codechina.csdn.net/A757291228/s-analysetooldemo](https://codechina.csdn.net/A757291228/s-analysetooldemo) # 環境 作者的環境說明如下: - 作業系統:Windows7 SP1 64 - python 版本:3.7.7 - 瀏覽器:谷歌瀏覽器 - 瀏覽器版本: 80.0.3987 (64 位) **如有錯誤歡迎指出,歡迎留言交流。** # 一、實現文字查重 ## 1.1 selenium安裝配置 由於使用的**selenium**,在使用前需要確保讀者是否已安裝**selenium**,使用**pip**命令,安裝如下: ```bash pip install selenium ``` 安裝完成 **Selenium** 還需要下載一個驅動。 - 谷歌瀏覽器驅動:驅動版本需要對應瀏覽器版本,不同的瀏覽器使用對應不同版本的驅動,點選[下載](http://chromedriver.storage.googleapis.com/index.html) - 如果是使用火狐瀏覽器,檢視火狐瀏覽器版本,點選 [GitHub火狐驅動下載地址](https://github.com/mozilla/geckodriver/releases/) 下載(英文不好的同學右鍵一鍵翻譯即可,每個版本都有對應瀏覽器版本的使用說明,看清楚下載即可) 安裝了**selenium**後新建一**python**檔名為**selenium_search**,先在程式碼中引入 ```python from selenium import webdriver ``` 可能有些讀者沒有把驅動配置到環境中,接下來我們可以指定驅動的位置(博主已配置到環境中): ```python driver = webdriver.Chrome(executable_path=r'F:\python\dr\chromedriver_win32\chromedriver.exe') ``` 新建一個變數**url**賦值為百度首頁連結,使用**get**方法傳入**url**地址,嘗試開啟百度首頁,完整程式碼如下: ```python from selenium import webdriver url='https://www.baidu.com' driver=webdriver.Chrome() driver.get(url) ``` 在小黑框中使用命令列執行python檔案(**windows**下): ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913153422932.png#pic_center) 執行指令碼後將會開啟谷歌瀏覽器並跳轉至百度首頁: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913153701614.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0E3NTcyOTEyMjg=,size_16,color_FFFFFF,t_70#pic_center) 這樣就成功使用**selenium**打開了指定網址,接下來將指定搜尋關鍵詞查詢得到結果,再從結果中遍歷到相似資料。 ## 1.2 selenium百度搜索引擎關鍵詞搜尋 在自動操控瀏覽器進行關鍵字鍵入到搜尋框前,需要獲取搜尋框元素物件。使用谷歌瀏覽器開啟百度首頁,右鍵搜尋框選擇檢視,將會彈出網頁元素(程式碼)檢視視窗,找到搜尋框元素(使用滑鼠在元素節點中移動,滑鼠當前位置的元素節點將會對應的在網頁中標藍): ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913154558227.png#pic_center) 在html程式碼中,id的值大多數情況下唯一(除非是打錯了),在此選擇id作為獲取搜尋框元素物件的標記。**selenium**提供了`find_element_by_id`方法,可以通過傳入**id**獲取到網頁元素物件。 ```python input=driver.find_element_by_id('kw') ``` 獲取元素物件後,使用send_keys方法可傳入需要鍵入的值: ```python input.send_keys('php基礎教程 第十一步 面向物件') ``` 在此我傳入了 “**php基礎教程 第十一步 面向物件**”作為關鍵字作為搜尋。執行指令碼檢視是否在搜尋框中鍵入了關鍵字。程式碼如下: ```python input.send_keys('php基礎教程 第十一步 面向物件') ``` 成功開啟瀏覽器並鍵入了搜尋關鍵字: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913155809329.png#pic_center) 現在還差點選“百度一下”按鈕完成最終的搜尋。使用與檢視搜尋框相同的元素檢視方法查詢“百度一下”按鈕的id值: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913155944191.png#pic_center) 使用`find_element_by_id`方法獲取到該元素物件,隨後使用click方法使該按鈕完成點選操作: ```python search_btn=driver.find_element_by_id('su') search_btn.click() ``` 完整程式碼如下: ```python 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() ``` 瀏覽器自動完成了鍵入搜尋關鍵字及搜尋功能: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020091316024143.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0E3NTcyOTEyMjg=,size_16,color_FFFFFF,t_70#pic_center) ## 1.3 搜尋結果遍歷 當前已在瀏覽器中得到了搜尋結果,接下來需要獲取整個**web**頁面內容,得到搜尋結果。使用selenium並不能很方便的獲取到,在這裡使用**BeautifulSoup**對整個**web**頁面進行解析並獲取搜尋結果。 **BeautifulSoup**是一個**HTML/XML**解析器,使用**BeautifulSoup**會極大的方便我們對整個**html**的資訊獲取。 使用**BeautifulSoup**前需確保已安裝。安裝命令如下: ```python pip install BeautifulSoup ``` 安裝後,在當前**python**檔案頭部引入: ```python from bs4 import BeautifulSoup ``` 獲取**html**文字可以呼叫page_source即可: ```python html=driver.page_source ``` 得到了**html**程式碼後,新建**BeautifulSoup**物件,傳入**html**內容並且指定解析器,這裡指定使用 **html.parser** 解析器: ```python soup = BeautifulSoup(html, "html.parser") ``` 接下來檢視搜尋內容,發現所有的結果都由一個**h**標籤包含,並且**class**為**t**: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913162748686.png#pic_center) **BeautifulSoup**提供了select方法對標籤進行獲取,支援通過類名、標籤名、id、屬性、組合查詢等。我們發現百度搜索結果中,結果皆有一個class ="t",此時可以通過類名進行遍歷獲取最為簡便: ```python search_res_list=soup.select('.t') ``` 在**select**方法中傳入類名**t**,在類名前加上一個點(**.**)表示是通過類名獲取元素。 完成這一步後可以新增print嘗試打印出結果: ```python print(search_res_list) ``` 一般情況下,可能輸出search_res_list為空列表,這是因為我們在瀏覽器解析資料渲染到瀏覽器前已經獲取了瀏覽器當前頁的內容,這時有一個簡單的方法可以解決這個問題,但是此方法效率卻不高,在此只是暫時使用,之後將會用其它效率高於此方法的程式碼替換(使用time需要在頭部引入): ```python time.sleep(2) ``` 完整程式碼如下: ```python 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) ``` 執行程式將會輸出內容: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913164607639.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0E3NTcyOTEyMjg=,size_16,color_FFFFFF,t_70#pic_center) 獲取到的結果為所有class為t的標籤,包括該標籤的子節點,並且使用點(.)運算髮可以獲取子節點元素。通過瀏覽器得到的搜尋內容皆為連結,點選可跳轉,那麼只需要獲取每一個元素下的a標籤即可: ```python for el in search_res_list: print(el.a) ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913165150520.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0E3NTcyOTEyMjg=,size_16,color_FFFFFF,t_70#pic_center) 從結果中很明顯的看出搜尋結果的a標籤已經獲取,那麼接下來我們需要的是提取每個a標籤內的href超連結。獲取href超連結直接使用列表獲取元素的方式獲取即可: ```python for el in search_res_list: print(el.a['href']) ``` 執行指令碼成功得到結果: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913165701500.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0E3NTcyOTEyMjg=,size_16,color_FFFFFF,t_70#pic_center) 細心的讀者可能會發現,這些獲取到的結果中,都是baidu的網址。其實這些網址可以說是“索引”,通過這些索引再次跳轉到真實網址。由於這些“索引”不一定會變動,並不利於長期儲存,在此還是需要獲取到真實的連結。 我們呼叫js指令碼對這些網址進行訪問,這些網址將會跳轉到真實網址,跳轉後再獲取當前的網址資訊即可。呼叫execute_script方法可執行js程式碼,程式碼如下: ```python for el in search_res_list: js = 'window.open("'+el.a['href']+'")' driver.execute_script(js) ``` 開啟新的網頁後,需要獲取新網頁的控制代碼,否則無法操控新網頁。獲取控制代碼的方法如下: ```python handle_this=driver.current_window_handle#獲取當前控制代碼 handle_all=driver.window_handles#獲取所有控制代碼 ``` 獲取控制代碼後需要把當前操作的物件切換成新的頁面。由於開啟一個頁面後所有頁面只有2個,簡單的使用遍歷做一個替換: ```python handle_exchange=None#要切換的控制代碼 for handle in handle_all:#不匹配為新控制代碼 if handle != handle_this:#不等於當前控制代碼就交換 handle_exchange = handle driver.switch_to.window(handle_exchange)#切換 ``` 切換後,操作物件為當前剛開啟的頁面。通過current_url屬性拿到新頁面的url: ```python real_url=driver.current_url print(real_url) ``` 隨後關閉當前頁面,把操作物件置為初始頁面: ```python driver.close() driver.switch_to.window(handle_this)#換回最初始介面 ``` 執行指令碼成功獲取到真實url: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913172213711.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0E3NTcyOTEyMjg=,size_16,color_FFFFFF,t_70#pic_center) 最後在獲取到真實url後使用一個列表將結果儲存: ```python real_url_list.append(real_url) ``` 這一部分完整程式碼如下: ```python 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基礎教程 第十一步 面向物件**”的內容。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913174603697.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0E3NTcyOTEyMjg=,size_16,color_FFFFFF,t_70#pic_center) 在程式碼中編寫一個函式為獲取文字內容: ```python def read_txt(path=''): f = open(path,'r') return f.read() src=read_txt(r'F:\tool\textsrc\src.txt') ``` 為了方便測試,這裡使用是絕對路徑。 獲取到文字內容後,編寫餘弦相似度的對比方法。 ## 1.5 餘弦相似度 相似度計算參考文章[《python實現餘弦相似度文字比較》](https://www.cnblogs.com/zuixime0515/p/9206861.html),本人修改一部分從而實現。 本文相似度對比使用餘弦相似度演算法,一般步驟分為分詞->向量計算->計算相似度。 新建一個python檔案,名為Analyse。新建一個類名為Analyse,在類中新增分詞方法,並在頭部引入jieba分詞庫,以及collections統計次數: ```python from jieba import lcut import jieba.analyse import collections ``` Count方法: ```python #分詞 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方法,使詞合併方便之後的向量計算: ```python #詞合併 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 ``` 合併方法很簡單不再做解釋。接下來新增向量計算方法: ```python # 得出文件向量 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 ``` 最後新增相似度計算方法: ```python 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) ``` 相似度方法接收兩個向量,隨後計算相似度並返回。為了程式碼冗餘度少,在這裡先簡單的新增一個方法,完成計算流程: ```python 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類的完整程式碼如下: ```python 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,並且新建物件: ```python from Analyse import Analyse Analyse=Analyse() ``` 在遍歷搜尋結果中新增獲取新開啟後的頁面的網頁內容: ```python time.sleep(5) html_2=driver.page_source ``` 使用 `time.sleep(5)`是為了等待瀏覽器能夠有時間渲染當前web內容。獲取到新開啟的頁面內容後,進行相似度對比: ```python Analyse.get_Tfidf(src,html_2) ``` 由於返回的是一個值,使用print輸出: ```python print('相似度:',Analyse.get_Tfidf(src,html_2)) ``` 完整程式碼如下: ```python 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) ``` 執行指令碼: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200913184735632.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0E3NTcyOTEyMjg=,size_16,color_FFFFFF,t_70#pic_center) 結果顯示有幾個高度相似的連結,那麼這幾個就是疑似抄襲的文章了。 以上是完成基本查重的程式碼,但是相對於說程式碼比較冗餘、雜亂,接下來我們優化一下程式碼。 # 二、程式碼優化 通過以上的程式程式設計,簡要步驟可以分為:獲取搜尋內容->獲取結果->計算相似度。我們可以新建三個類,分別為:Browser、Analyse(已新建)、SearchEngine。 Browser用於搜尋、資料獲取等;Analyse用於相似度分析、向量計算等;SearchEngine用於不同搜尋引擎的基本配置,因為大部分搜多引擎的搜尋方式較為一致。 ## 2.1Browser 類 **初始化** 新建一個python檔案,名為Browser,新增初始化方法: ```python 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()`為獲取搜尋引擎的配置,不同搜尋引擎的輸入框、搜尋按鍵不一致,通過不同的配置資訊實現多搜尋引擎搜尋。 **新增搜尋方法** ```python #搜尋內容寫入到搜素引擎中 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']`通過初始化方法得到對應的搜尋引擎配置資訊,直接獲取資訊得到元素。 **點選搜尋** ```python #搜尋框點選 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。 **獲取搜尋結果與文字** ```python #獲取搜尋結果與文字 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**的網頁元素,**id**為**page**的網頁元素為分頁按鈕的標籤id,如果未獲取表示當前web頁並未載入完全,等待時間為`timeout=30`30秒,如果已過去則跳過等待。 以上程式碼中並不做相似度對比,而是通過 `res_link[real_url]=self.browser.page_source` 將內容與**url**存入字典,隨後返回,之後再做相似度對比,這樣編寫利於之後的功能擴充套件。 **開啟目標搜尋引擎進行搜尋** ```python #開啟目標搜尋引擎進行搜尋 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**方法即可實現之前的所有操作,不用暴露過多簡化使用。 完整程式碼如下: ```python 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**類主要用於不同搜尋引擎的配置編寫。更加簡便的實現搜尋引擎或相似業務的擴充套件。 ```python #搜尋引擎配置 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如何使用 首先引入兩個類: ```python from Browser import Browser from Analyse import Analyse ``` 新建一個方法讀取本地檔案: ```python def read_txt(path=''): f = open(path,'r') return f.read() ``` 獲取檔案並新建資料分析類: ```python src=read_txt(r'F:\tool\textsrc\src.txt')#獲取本地文字 Analyse=Analyse() ``` 配置資訊字典編寫: ```python #配置資訊 conf={ 'kw':'php基礎教程 第十一步 面向物件', 'engine':'baidu', } ``` 新建Browser類,並傳入配置資訊: ```python drvier=Browser(conf) ``` 獲取搜尋結果及內容 ```python url_content=drvier.search()#獲取搜尋結果及內容 ``` 遍歷結果及計算相似度: ```python for k in url_content: print(k,'相似度:',Analyse.get_Tfidf(src,url_content[k])) ``` 完整程式碼如下: ```python 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**檔案檔名為關鍵字,內容為該名稱的文章內容。類程式碼如下: ```python 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_content**與**get_text**。**get_text**為獲取目錄下所有txt檔案路徑,通過**get_content**獲取到詳細文字內容,返回**local_text**;**local_text**鍵為檔名,值為文字內容。 ## 3.2BrowserManage類 在**Browser**類檔案中新增一個**BrowserManage**類繼承於**Browser**,新增方法: ```python #開啟目標搜尋引擎進行搜尋 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**類中新增下一頁方法,使搜尋內容時能夠獲取更多內容,並且可指定獲取結果條數: ```python #下一頁 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**方法的修改了部分內容,添加了增加結果條數指定、下一頁內容獲取以及白名單設定更改過後的程式碼如下: ```python #獲取搜尋結果與文字 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(r