做者: 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标签包含,而且class为t:

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)
运行脚本:

结果显示有几个高度类似的连接,那么这几个就是疑似抄袭的文章了。以上是完成基本查重的代码,可是相对于说代码比较冗余、杂乱,接下来咱们优化一下代码。
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的网页元素,id为page的网页元素为分页按钮的标签id,若是未获取表示当前web页并未加载彻底,等待时间为timeout=30
30秒,若是已过去则跳过等待。以上代码中并不作类似度对比,而是经过 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_content与get_text。get_text为获取目录下全部txt文件路径,经过get_content获取到详细文本内容,返回local_text;local_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方法遍历文本,使用文件名看成关键字进行搜索,而且将搜索内容与当前文本作类似度对比,最后返回结果。结果以下:

博主目录下文件以下:

类似度分析部分以上为主要内容,工具以后将会丢GitHub及csdn的代码仓库中,使用的无头模式,本篇所讲的内容为通常实现。
全部完整的代码以下: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())
福利时间
当当双十一大促,为你们谋了点福利
每满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源创计划”,欢迎正在阅读的你也加入,一块儿分享。