首页 > 编程语言 >使用杰卡德算法计算公众号文章的近似值

使用杰卡德算法计算公众号文章的近似值

时间:2022-11-30 15:11:24浏览次数:34  
标签:jieba set self 算法 keywords 近似值 卡德 idf path

客户提供了公众号文章的永久链接,并在远程数据库中保存了原创的文章,要求采集目标公众号文章和原创文章有多少重复的,以便判定是否侵权。

程序设计

每天都有大几千的公众号文章url保存到远程数据库中,并要求及时统计近似值,原则上当前的url当前都要消化完毕,如果能在1个小时内消化更佳。大几千的url也不算什么,不过为了满足客户的时效性,打算使用三进程:

  • 进程1 数据库交互
    该进程负责和数据库交互,读取要爬取的url、采集到数据后写入数据库一级更新其他必要的数据
  • 进程2 数据处理
    请求url拿到数据后的数据结构组织、近似值计算以及保存到数据库之前的数据去重
  • 进程3 url请求
    该进程只负责请求页面拿数据,提取必要的信息,如文章标题、公众号名称和id、文章发布日期,以及是否原创和是否有作者、公众号二维码等信息

url请求

使用requests库即可,请求公众号文章不要求请求头和cookies,不要太简单,只需要注意

  • 纯图的文章和纯视频的文章时,标题和内容样式的class和id与文字文章有所不同
  • 二维码和文章发布时间是js加载的,需要从页面正则提取
    文章发布时间 提取正则
re.compile('if\(!window.__second_open__\){e\(0,"(.*?)",0,document.getElementById\("publish_time"\)', re.X)

二维码url提取正则

# 二维码
def get_qrcode(self):
	url_patter = re.compile('<meta property="og:url" content="(.*?)"')
	r1 = re.findall(url_patter, self.text)
	if r1:
		url_groups = re.search('biz=(.*?)&amp;mid=(.*?)&amp;idx=(.*?)&amp;sn=(.*?)&amp', self.text)
		url = 'https://mp.weixin.qq.com/mp/qrcode?scene=10000004&size=200&__biz={0}&mid={1}&idx={2}&sn={3}'.format(url_groups.group(1), url_groups.group(2), url_groups.group(3), url_groups.group(4))
		return url

此外,在url请求环节,还设计了单线程和多线程+是否使用代理的双模式,通过txt配置来控制,客户可以使用单线程、多线程,多线程时,还可以使用当前地址或者代理。

数据处理

没啥特别的,近似值算法上,使用了杰卡德算法:A和B交集/A和B的并集,不过客户强烈要求使用A和B的交集/B,其中A是原创文章,B是目标文章。贴一下代码:

class JaccardSimilarity(object):
    """
    jaccard相似度
    """
    def __init__(self, content_x1, content_y2):
        self.s1 = content_x1 # 原文章
        self.s2 = content_y2 # 目标文章

    @staticmethod
    def extract_keyword(content):  # 提取关键词
        # # 正则过滤 html 标签 忽略原html标签和转义
        # re_exp = re.compile(r'(<style>.*?</style>)|(<[^>]+>)', re.S)
        # content = re_exp.sub(' ', content)
        # # html 转义符实体化
        # content = html.unescape(content)
        # 切割
        seg = [i for i in jieba.cut(content, cut_all=True) if i != '']
        # 提取关键词
        keywords = jieba.analyse.extract_tags("|".join(seg), topK=200, withWeight=False)
        return keywords

    def main(self):
        # 去除停用词
        jieba.analyse.set_stop_words('stopwords.txt')

        # 分词与关键词提取
        keywords_x = self.extract_keyword(self.s1)
        keywords_y = self.extract_keyword(self.s2)

        # jaccard相似度计算
        intersection = len(list(set(keywords_x).intersection(set(keywords_y))))
        union = len(list(set(keywords_x).union(set(keywords_y))))
        # union = len(list(set(keywords_x).union(set(keywords_y)))) # 原算法union为并集
        
        # a = len(set(keywords_x))
        b = len(set(keywords_y))
        # 除零处理
        # sim = float(intersection)/union if union != 0 else 0 # 原算法 xy交集/xy并集
        sim = float(intersection)/b if b != 0 else 0
        return sim

注:算法实现是网上找的,不记得是从哪个网友的博客上复制的了。( ╯□╰ )。

这里又有点小问题,因为最终成程序要打包成exe,jieba模块在打包时依赖词典等文件,所以需要简单设置:
1、dict.txt依赖,jieba提供了设置词典路径的接口,直接使用。
先从jieba安装目录下找到dict.txt,拷贝到程序的当前目录,然后使用代码加载一次:

import jieba
jieba.set_dictionary('./dict.txt')
jieba.initialize()

2、idf.txt依赖
虽然官方也提供了接口jieba.analyse.set_idf_path()来设置idf.txt的路径,奈何怎么设置都不成功,也看了源码,analyse貌似也没有重载的入口,也有可能我找不到的原因。总之,最后通过修改源码的方式才实现了打包时文件的依赖。
步骤1 先找到:

class TFIDF(KeywordExtractor):

    def __init__(self, idf_path='.\idf.txt'):  # 原代码中idf_path=None 现修改为当前目录的idf.txt
        self.tokenizer = jieba.dt
        self.postokenizer = jieba.posseg.dt
        self.stop_words = self.STOP_WORDS.copy()
        self.idf_loader = IDFLoader(idf_path or DEFAULT_IDF)
        self.idf_freq, self.median_idf = self.idf_loader.get_idf()

    def set_idf_path(self, idf_path):
        new_abs_path = _get_abs_path(idf_path)
        if not os.path.isfile(new_abs_path):
            raise Exception("jieba: file does not exist: " + new_abs_path)
        self.idf_loader.set_new_path(new_abs_path)
        self.idf_freq, self.median_idf = self.idf_loader.get_idf()

步骤2 主程序文件中再加载

import jieba.analyse
jieba.analyse.set_idf_path('./idf.txt')

也尝试过通过配置pyinstaller.spec文件,试图讲txt文件一起打包进来,最终没成功,虽然才选择了这个最简单的方式:改源码。

数据库交互

这个没啥要记录的,使用pymysql操作远程mysql而已。在优化之前封装的pymysql操作库时,简化了一个模块:

def insert(self, table_name=None, single=True, data_list: list = []):
	cur = self.conn.cursor()
	for data in data_list:
		sql = f'insert into {table_name}({",".join([key for key in data.keys()])}) values({",".join(["%s" for _ in range(len(data.keys()))])})'
		cur.execute(sql, data)
		self.conn.commit()
		cur.close()

把insert中的sql语句给优化了,",".join([key for key in data.keys()])",".join(["%s" for _ in range(len(data.keys()))])看似优雅,实则难懂。

标签:jieba,set,self,算法,keywords,近似值,卡德,idf,path
From: https://www.cnblogs.com/mooremok/p/16938523.html

相关文章

  • 【转】一致性hash算法与server列表维护
      普通的hash算法有个很大的问题:当hash的"模数"发生变化时,整个hash数据结构就需要重新hash,重新hash之后的数据分布一定会和hash之前的不同;在很多场景下,"模数"的变......
  • 【转载】一致性hash算法与server列表维护(备忘)
    普通的hash算法有个很大的问题:当hash的"模数"发生变化时,整个hash数据结构就需要重新hash,重新hash之后的数据分布一定会和hash之前的不同;在很多场景下,"模数"的变化时必......
  • java排序算法
     一.冒泡排序特点:实现简单,无额外空间消耗,速度较慢,适合数据较少的场景,复杂度为O(N^2)思路:每一轮比较都从头开始,然后两两比较,如果左值比右值大,则交换位置,每一......
  • C++ 不知算法系列之初识动态规划算法思想
    1.概述动态规划算法应用非常之广泛。对于算法学习者而言,不跨过动态规划这道门,不算真正了解算法。初接触动态规划者,理解其思想精髓会存在一定的难度,本文将通过一个案例,抽......
  • 【推荐系统算法实战】 Spark :大数据处理框架
    Spark简介​​http://spark.apache.org/​​​​https://github.com/to-be-architect/spark​​与​​Hadoop​​​和​​Storm​​​等其他大数据和​​MapReduce​​......
  • 【推荐系统算法实战】 基于网页的 Notebook:Zeppelin 交互式数据分析
    【推荐系统算法实战】基于网页的Notebook:Zeppelin交互式数据分析如果有一个工具,可以让你在同一个Web页面上写Shell代码,Python代码,Scala代码,你想要么?如果还可以执行PySpa......
  • 每日算法之数值的整数次方
    JZ16数值的整数次方描述实现函数doublePower(doublebase,intexponent),求base的exponent次方。注意:1.保证base和exponent不同时为0。2.不得使用库函数,同时不需......
  • 反向传播算法
    0梯度更新函式1梯度神经网络参数如下:θ={w1,w2,...,b1,b2,...}权重梯度如下:为了更好且有效的计算梯度,我们使用反向传播算法。2链式法则 3反向传播......
  • 《基于深度学习算法的人脸识别应用研究》论文笔记十二
    一、基本信息标题:基于深度学习算法的人脸识别应用研究时间:2021来源:厦门理工学院关键词: 深度学习;人脸识别;卷积神经网络;级联网络;数据增强;二、研究内容问题定义:围......
  • 《面向监控视频、复杂场景的人脸识别算法研究》论文笔记十三
    一、基本信息标题:面向监控视频、复杂场景的人脸识别算法研究时间:2020来源:西南交通大学关键词: 人脸识别;数据库清理和融合;神经网络;特征融合;二、研究内容问题定义:......