合工大Python语言与系统设计大作业

Python选修课程序设计报告

设计题目: 利用flask+jQuery实现包含前后端的文本相似度分析项目

作者: moonchild

专 业:计算机科学与技术

完成日期:2022/11/9

文章目录

  • Python选修课程序设计报告
      • 系统设计背景
        • 问题描述:
        • 编程任务:
      • 设计
        • 1. 设计思想
        • 2. 设计表示
        • 3. 核心算法
      • 用户手册
      • 测试
      • 进一步改进
      • 感想
      • 不想墨迹,直接看代码

系统设计背景

问题描述:

如何在大量的文本之中查找到最具代表性的特征文本?如何在海量文本之中去除重复内容?这是NLP(Nature Language Processing)领域的重要方向.也就是计算文本之间的相似度.其具有广泛的应用场景,从论文查重到搜索引擎排除相似内容,不一而足.

这是一个利用后端利用Python之中的第三方库+前端jQuery+bootstrap实现的分析两段文本之间的相似度的项目.用户可以在可视界面中上传txt文件或者输入文本, 经后端运算之后返回前端.

编程任务:

  1. 使用余弦定理计算两段文本之间的相似度.适用于短文本.此处涉及到两个python库:
    1. jieba: 中文分词
    2. scipy.spatial.distance.cosine,主要是余弦定理的计算,此处也可以自己手动造轮子来计算
    3. 主要思路:
      1. 读取用户输入的文本
      2. 分词
      3. 对两段文本进行预处理,取两个文本主要词的并集,并且根据该并集生成
        每一段文本的特征向量
      4. 接下来便是对特征向量计算余弦值,也就是我们需要的相似度
  2. simhash方法:
    1. 这部分则主要适用于长文本,对于使用余弦定理来计算大量特征向量之间的相似度而言时空复杂度较高
    2. 直接使用了网上开源的simhash算法,之后进行了封装,使其更加适用于本次大作业
  3. 前端界面:用户输入或者上传文件来与现有的语料库进行重复度分析,并且将用户上传的文本添加到语料库之中
    1. 前端主要使用了html,css和jQuery来实现,jQuery实现了前后端通信
    2. 后端使用flask框架
    3. 主要思路:
      1. 用户上传文件或者输入文本
      2. 后端接收到前端的请求,并且将用户上传的文本或者文件进行处理,并且将处理后的文本添加到语料库之中
      3. 后端将处理后的文本与语料库中的文本进行相似度分析,并且返回给前端
      4. 前端接收到后端的返回值,并且将其展示在前端界面上.

设计

1. 设计思想

  1. 适用余弦定理计算文本相似度:
  2. Simhash方法计算文本相似度

2. 设计表示

类名 成员类别 成员名 描述
ConsineSimilarity 函数 init() 构造函数
@property 函数 Similarity() 获取用户输入的两段文本之间的相似度
@staticmethod 函数 preprocessing() 对用户输入的文本进行预处理
@staticmethod 函数 oneHot() 预处理,生成离散的One Hot编码
属性 string1 文本1
属性 string2 文本2
SimhashSimilarity
函数 init() 构造函数
@property 函数 getSimilarity 获取两个文本之间的相似度
属性 string1 文本1
属性 string2 文本2

2.1 后端函数:

数据类型 函数名称 描述
@app.router(“/”) index() 主页
@app.router(“/text/file/”) uploadFile() 用于上传文本的接口函数
@app.router(“/text/”) getSimilarity() 获取用户输入的文本
bool isFileExtensionAllowed() 判断用户上传文件的扩展名是否合法.本项目之中限制用户上传的文件格式为‘.txt’
str readFile() 读取文本文件,并且返回与处理以后的文本
None ProcessInput 考虑到Simhash方法在文本较短情况下的效果并不理想,而余弦定理方法处理效率较低,因此,文本较短时,此时不如使用余弦方法来进行计算.因此,该函数之中根据用户输入的文本的长度分别到用余弦方法和Simhash方法

2.2 前端函数

函数名 函数用途
uploadText() 该方法之中,调用jQuery提供的ajax()方法来像后端服务器发送文本
uploadFile() 该方法之中,调用jQuery提供的ajax()方法,将文件添加到表单对象之中,发送到后端server
一众隐函数 用于实现对界面按钮以及文本域的监听

3. 核心算法

  1. 余弦定理计算文本相似度

    1. 主要代码:
        def __init__(self, string1, string2):  # 如何判断用户输入的是一段文本还是一个文件路径????
            # 先假设用户输入的一段文本,首先去空格,去标点符号
            self.text1 = self.preprocessing(string1)
            self.text2 = self.preprocessing(string2)
    
        @staticmethod
        def preprocessing(text: str) -> str:
            return text.replace('\n', '').replace('\t', '').replace('\r', '').replace(' ', '')
    
        @staticmethod
        def oneHot(wordDict, keyWords):  # 预处理,生成离散的oneHot()编码
            oneHotCode = [0 for _ in range(len(wordDict))]
            for word in keyWords:
                oneHotCode[wordDict[word]] = 1
            return oneHotCode
    
        # 计算余弦相似度
        @property
        def Similarity(self):
            # extract_tags本身返回的就是出现频率最高的20个词,那么接下来的union至多40个单词
            text1 = jieba.analyse.extract_tags(self.text1)
            text2 = jieba.analyse.extract_tags(self.text2)
    
            union = set(text1).union(set(text2))  # 去重取并集
    
            # 为每个词添加索引,使用字典
            wordDict = dict(zip(union, range(0, len(union))))
            text1OneHotCode = self.oneHot(wordDict, text1)
            text2OneHotCode = self.oneHot(wordDict, text2)
    
            # 计算余弦相似度,使用scipy包里的cosine_similarity
            # return 1 - cosine_similarity([text1OneHotCode, text2OneHotCode])
            try:
                sim = cosine_similarity([text1OneHotCode, text2OneHotCode])
    
    1. 算法思想:
    • 根据高中数学知识可以得知,通过余弦定理可以计算两个向量之间的夹角,同理,如果我们把一篇文章的特征词看作向量的话,那么如果两组特征向量之间的夹角小容易推知这两篇文章较为相似.我们首先利用jieba库提取出文章的关键词,之后计算出文章的关键词的OneHot()编码,在调用scipy提供的cosine_similarity()方法,可以得出文本之间的相似度
  2. simhash方法

    1. 源代码:
    class SimhashSimilarity:
        """
            调用Simhash库,计算文本相似度
        """
    
        def __init__(self, string1, string2):
            self.text1 = string1
            self.text2 = string2
    
        @property
        def getSimilarity(self):
            # 生成Simhash对象
            simhash1 = Simhash(self.text1)
            simhash2 = Simhash(self.text2)
    
            # 计算海明距离
            distance = simhash1.distance(simhash2)
            # 计算相似度
            similarity = 1 - distance / 64
            print("文本相似度:%.2f%%" % (similarity * 100))
            print()
            return similarity
    
    1. 算法思想
    • simhash算法对局部敏感,当文本相似时,计算所得hash数值仅有部分不同,而普通的hash方法对相似文本则会有天翻地覆的变化.因此,我们可以根据局部敏感哈希方法来判断文本的相似度.
    • 主要步骤: 1. 分词 2. 哈希 3.加权 4. 合并 5 降维 6.计算哈希值的海明距离.
    • 其实在实现的过程之中,考虑过爬取文本并且计算其哈希签名值群出道数据库之中作为语料库,但是时间紧促,再去学习数据库的操作时间来不及,因此还是设计了两段文件之间的相似度比较
  3. 前端JavaScript

    1. 源代码
    $(function () {
        let FileOrInput = 0   //判断用户是上传文件还是直接上传文本,1为默认状态,表示文本
        let file1 = undefined;
        let file2 = undefined;
        let File = $("#inputFile");
        let Text = $("#inputText");
        Text.css("display", "none");
        File.css("display", "block");
        $("#formFile1").change(function (event) {
            file1 = event.target.files[0];
            if (file1) {
                console.log(file1);
            }
        })
        $("#formFile2").change((event) => {
            file2 = event.target.files[0];
            if (file2) {
                console.log(file2);
            }
        })
    
        function uploadText(event) {
    
            let text = $("#floatingTextarea1").val();
            let text2 = $("#floatingTextarea2").val();
            console.log(text);
            console.log(text2);
            event.preventDefault();
            let Form = new FormData();
            Form.append("text1", text);
            Form.append("text2", text2);
            $.ajax({
                url: "/text/",
                type: "POST",
                data: Form,
                processData: false,
                contentType: false,
                success: (result) => {
                    alert("文本的相似度为:  " + result*100 + "%");
                }
            })
    
        }
    
        function uploadFile(event) {
            let Form = new FormData();
            Form.append("file1", file1);
            Form.append("file2", file2);
            $.ajax({
                url: "/text/file/",
                method: "POST",
                data: Form,
                processData: false,
                contentType: false,
                success: (res) => {
                    console.log(res);
                    alert('文件的相似度为:  ' + res*100 + '%');
                },
                error: (err) => {
                    console.log(err);
                    console.log(err.status);
                }
    
            })
        }
    
        $("#CommitBtn").click(function (event) {
            if (FileOrInput)
                uploadText(event)
            else {
                console.log(file1);  //此处可以获取到file
                console.log(file2);
                uploadFile(event);
            }
        })
    
        $("#btnradio1").click(function (event) {
            // 点击了按钮一以后,表示用户要提交文件,那么文本框隐藏
            console.log("click radio1")
            Text.css("display", "none");
            File.css("display", "block");
            FileOrInput = 0;
            console.log(FileOrInput);
        })
        $("#btnradio2").click(function (event) {
            console.log("click radio 2")
            File.css("display", "none");
            Text.css("display", 'block');
            FileOrInput = 1;
        })
    });
    
    1. 代码说明
      • 用户可以选择提交文件还是文本.
      • 主要使用jQuery提供的ajax()方法来送出数据.
  4. flask框架

    1. 源代码:
    import os
    import webbrowser
    
    from flask import Flask, redirect, request, render_template, flash
    from werkzeug.utils import secure_filename
    
    import config
    from main import SimhashSimilarity, CosineSimilarity, readFile, ProcessInput
    
    app = Flask(__name__)
    # 添加配置文件
    app.config.from_object(config)
    app.config.from_pyfile('config.py')
    
    
    # 注册蓝图模块
    # app.register_blueprint(bp)
    
    @app.route('/')
    def index():
        # 在这个函数之中将页面返回
        return render_template("index.html")
    
    
    def isFileExtensionAllowed(filename: str) -> bool:
        if filename.rsplit('.', 1)[-1].lower() in config.ALLOWED_EXTENSION:  # 获取文件的扩展名
            return True
        else:
            return False
    
    
    @app.route("/text/file/", methods=["GET", "POST"])
    def uploadFile():
        file1 = request.files.get("file1")
        file2 = request.files.get("file2")
        fileList = os.listdir(config.UPLOAD_FOLDER)
        num = fileList.count(file1.filename)
        if num > 0:
            file1.filename = file1.filename.split('.')[0]+'({0})'.format(num)+'.txt'
        num = fileList.count(file2.filename)
        if num > 0:
            file2.filename = file2.filename.split('.')[0]+'({0})'.format(num)+'.txt'
        if file1 and file2:
            if isFileExtensionAllowed(file1.filename) and isFileExtensionAllowed(file2.filename):
                file1.save(os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(file1.filename)))
                file2.save(os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(file2.filename)))
                text1 = readFile(os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(file1.filename)))
                text2 = readFile(os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(file2.filename)))
                return str(ProcessInput(text1, text2))
            else:
                return "Error"
    
    
    # 此路由用于获取用户输入的文本
    @app.route("/text/", methods=["GET", "POST"])
    def getSimilarity():
        text1 = request.form.get("text1")
        text2 = request.form.get("text2")
        print(text1, text2)
        res = ProcessInput(text1, text2)
        print(res)
        return res
    
    
    
    
    if __name__ == '__main__':
        webbrowser.open("http://127.0.0.1:5000")
        app.run()
    
    
    1. 代码说明
    • 主要是几个路由函数,分别为用户提供了几个传送数据的接口
    • 对于重复提交的文件会在文件名添加(1)(2)这样的后缀

用户手册

用户可以选择提交文本还是提交txt文件,点击提交之后便会返回两段文本之间的相似度

测试

  1. 测试文本
    测试文本选择了新华社的两篇报道,但是CSDN讲内容比较敏感,所以在此处删除了测试文本
  • 文本1:新华社<坚持人民至上>的报道
  1. 测试结果

输出结果: 文本相似度为 51.5625%

进一步改进

  1. 理想中的版本是添加了利用python强大的网页爬取能力在主流网站上爬取一些文本存到数据库之中,这样用户可以上传单一文本来判断该文本的重复度,但是受到了几点限制:数据库操作,网页爬虫的学习在期中考试之前这一段时间很难完成,而且想要做到一个完善的查重程序需要很丰富的语料库,这个在短期时间内很难做到

  2. 仅仅是单页面,较为简陋,而且操作逻辑\交互不够丰富,用户体验较差.时间受限,仅仅完成了单页面,尽管借助了bootstrap丰富的组件库,但是对于页面的设计,还是没有一个很好的思路.

  3. 在写完大作业的一段时间之后,我了解到了python之中的gensim库,该库可以实现导入中文词向量模型,封装了使用余弦定理计算文本相似度的算法.不止于此,其中还包含更丰富的文本处理的算法.比如,返回几个词序列之中最不相似的一个词,再比如对一个词进行联想,返回其他类似的词语.等等等等.如果以后还有机会做类似的项目,会优先考虑使用gensim

  4. 可以让用户上传两个网站URL,通过爬虫获取网站文本内容,计算二者文本的相似度并返回.

感想

  1. Python生态实在是丰富,人生苦短,我用python
  2. 继续练习了前端页面的编写,ajax函数发送数据的编写,暑假期间学习ajax的痛苦记忆犹新.由于server和前端页面都部署在本地,不涉及到跨域的问题,这也显得稍微容易了一些.
  3. 初步接触了后端的编写,初步了解了flask的语法,工作方式
  4. 这也是我Python必修课的课程大作业,由于时间仓促,当时对Python的掌握与使用也不够深入,所以在设计上有很多的瑕疵.可能这个项目和选修课课程核心: 数据的分析和处理 关联不是很强,仅仅在文本相似度计算方面有些重合.的确,这是一个更加偏向工程化的项目,也是对暑假期间前端内容的复习,并且学习了一下后端是如何编写的.

不想墨迹,直接看代码

将代码上传到了Github上.可以无需解压zip文件,直接点击下面的链接进行访问

moonchildink/TextSimilarity

你可能感兴趣的:(python,flask,开发语言)