wechat工具导出的html类型的聊天记录,做情感分析(上)

from bs4 import BeautifulSoup
import os, re, warnings, pickle, jieba, numpy as np, pandas as pd
from gensim.models import KeyedVectors
import traceback, logging
from tkinter.messagebox import *
import tkinter, functools
from scipy.spatial.distance import pdist

"""
initial parameters:
        num_words  只使用前2599999个中文词做测试----目前作为测试,生产过程可以全部使用
                   在这个预训练词向量模型中,一共有大约260万词汇量
        embedding_dim   词向量数----该值是基于sgns.zhihu.bigram中的维度来设定
        path_checkpoint 建立一个权重的存储点
        SAVE_HTML_FILE_PATH 保存html的文件路径
        save_excel   最后预测完成所生成的excel文件
        similarity_value 比较两个句子的相似程度  取值0-1 值越小越代表相似度越低
        high_weight_sentence  高权重句子分类后的excel表名称
        sentiment_hot_code 3 6 9 12 (一般聊天, app和群内问题, 知识点问题, 机构评价老师评判)
"""
"""
analysis principle:
     first-> 得到的数据通过sgns.zhihu.bigram 得到该句子的向量(每一个单词词向量累加 )
     second->  归一化处理所有数据
     third-> 使用模型预测
"""
num_words = 2599999
embedding_dim = 300
path_checkpoint = 'Model'
save_html_file_path = 'datas'
# 本次采用的是 sgns.zhihu.bigram 词向量模型  大家可以网上下载
word_vector_model = 'sgns.zhihu.bigram'
save_excel = '预测后的分类.xlsx'
high_weight_sentence = 'highWeightSentence.xlsx'
similarity_value = 0.7
sentiment_hot_code = 6
# 忽略一些 deprecate 警告
warnings.filterwarnings("ignore")

# 将所有维度保存成字典 字典是{'句子内容':[句子维度, 句子分类]}
all_of_sentence_dimension_dictionary = {}


def use_BeautifulSoup_invoke_html_file_contents_final_writeTo_excel():

    dir_contains_file = os.listdir(save_html_file_path)
    all_data_reverseTo_dic = {'sentiments': [], 'content': [], }
    # 打开模型
    with open(path_checkpoint, 'rb') as file:
        model = pickle.load(file)
    print('*****************************************************')
    print('Invoke WordVector')
    cn_model = KeyedVectors.load_word2vec_format(word_vector_model, binary=False, unicode_errors="ignore")
    for flag, i in enumerate(dir_contains_file):
        # 拼接正常的文件地址
        file_path = save_html_file_path + '/' + i
        # 打开文件,read()
        with open(file_path, 'rb') as o:
            binary_datas = o.read()
        # 再利用BeautifulSoup进行html节点拆分,通过搜索'pre'元素得到想要的聊天内容
        print('*****************************************************')
        print('Parsing HTML Tree Constructor -> id (%d) page' % flag)
        initial_text = BeautifulSoup(binary_datas, 'lxml').select('pre')
        # 将得到的'pre'标签内的所有数据进行遍历
        for j in initial_text:
            # 将dom树中的数据提取成字符串
            extract_text = j.text.strip()
            if '@' in extract_text:
                # 第一步处理  由于微信中@后边一般都是人名,对语言数据起到噪点作用,需要提前剔除。
                first_execute_text = ''.join(re.split(r'@.+',
                                                      ''.join(re.split(r'@.+ +',
                                                                       ''.join(re.split(r'@.+\u2005+', extract_text))
                                                                       )
                                                              )
                                                      )
                                             )
                # 保留正常文字,剔除符号
                execute_after_text = ''.join(re.findall('[\u4e00-\u9fa5a-zA-Z0-9]+', first_execute_text, re.S))
                # 如果遇到空字符串或带有XML字眼的str都是不需要的文字
                # 则继续遍历
                if execute_after_text == '' or 'xml' in execute_after_text:
                    continue
                # 将需要预测的语言和词向量模型以及模型传入
                sentiment = predict_sentiment(execute_after_text, cn_model, model)
                all_data_reverseTo_dic['sentiments'].append(sentiment)
                all_data_reverseTo_dic['content'].append(execute_after_text)
            elif 'http' in extract_text or 'xml' in extract_text:
                continue
            else:
                execute_after_text = ''.join(re.findall('[\u4e00-\u9fa5a-zA-Z0-9]+', extract_text, re.S))
                if execute_after_text == '':
                    continue
                # 使用预测方法将语句预测出情感并添加到字典中
                sentiment = predict_sentiment(execute_after_text, cn_model, model)
                all_data_reverseTo_dic['sentiments'].append(sentiment)
                all_data_reverseTo_dic['content'].append(execute_after_text)

    # 保存数据
    all_of_datas_and_cosine_similarity_after_compared_collection_dictionary = {}
    # 遍历循环所有句子的维度并将句子维度一一对比
    for key, values in zip(all_of_sentence_dimension_dictionary.keys(), all_of_sentence_dimension_dictionary.values()):
        # 设置一个counts得分
        cosine_similarity_compara_result_counts = 0
        if values[1] == sentiment_hot_code:
            for j in all_of_sentence_dimension_dictionary.values():
                # 比较句子维度的余弦值
                if j[1] == sentiment_hot_code:
                    cosine_similarity_value = 1 - pdist(np.vstack([values[0], j[0]]), 'cosine')
                # 如果一一列举时,两个句子的余弦相似度大于similarity_value说明两句子相似程度非常高,则把余弦得分+1
                    if cosine_similarity_value >= similarity_value:
                        cosine_similarity_compara_result_counts += 1
        # 将余弦值大于0.7的 cosine_similarity_compara_result_counts (计数) 插入到字典
        # 该字典是  用每一个句子和其他句子比较,将余弦值大于similarity_value的记录。该字典的K是该句子,V是 [匹配后于其他句子余弦相似度比值大于similarity_value的个数, 该句子的分类(3是闲聊,6是app问题,9是专业问题,12是机构和老师赞贬)]
        all_of_datas_and_cosine_similarity_after_compared_collection_dictionary[key] = \
            [cosine_similarity_compara_result_counts, values[1]]
    # 利用排序工具将一个对象按照我们自定义的规则进行排列
    for t in sorted({k:v for k, v in all_of_datas_and_cosine_similarity_after_compared_collection_dictionary.items() if v[1] == 6}.items(),
            key=functools.cmp_to_key(custmor_Sort)
                   ):
        print(t)
    pd.DataFrame(all_data_reverseTo_dic).to_excel(save_excel)
    print('*****************************************************')
    print('All of Work Complete, Save A .xlsx file was %s' % save_excel)


"""
Tools  Method
"""


# 预测情绪
def predict_sentiment(text, cn_model, model):
    # 分词
    cut = jieba.cut(text)
    # 创建一个变量来保存所有句子
    sentence_vector = None
    for i, word in enumerate(cut):
        try:
            # 1. 很有可能得是: 一个词在词向量中并无对应,所以要try catch
            # 2. cn_model[word].copy() 得原因是 该情况瞎numpy 返回的数据类型只支持读不支持写 会抛出ValueError: output array is read-only
            #    所以使用cn_model[word].copy() 复制一份,在内存中独立占据空间,这样就可以操作了。
            #    或者 使用  sentence_vector = sentence_vector + cn_model[word]
            if sentence_vector is None:
                sentence_vector = cn_model[word].copy()
            else:
                # 词组 维度累加 得到一个最终句子的维度
                sentence_vector += cn_model[word].copy()
        except KeyError:
            continue
    if sentence_vector is not None:
        # 归一化处理
        sentence_vector_max = sentence_vector.max()
        sentence_vector_min = sentence_vector.min()
        new_sentence_vector = \
            np.array([(i - sentence_vector_min) / (sentence_vector_max - sentence_vector_min) for i in sentence_vector])
        result = model.predict([new_sentence_vector])
        # 将一个句子对应一个维度
        all_of_sentence_dimension_dictionary[text] = [sentence_vector, result[0]]
        return result[0]
    elif sentence_vector is None:
        # 如果句子维度是空的话,直接产生一个(300,)维度的数组,填补到句子维度中
        new_sentence_vector = np.zeros(embedding_dim, )
        result = model.predict([new_sentence_vector])
        # 将一个句子对应 [一个维度, 一个分类]
        all_of_sentence_dimension_dictionary[text] = [new_sentence_vector, result[0]]
        return result[0]


# 个人定义的比较工具
def custmor_Sort(x, y):
    return y[1][0] - x[1][0]


if __name__ == '__main__':
    # try:
    use_BeautifulSoup_invoke_html_file_contents_final_writeTo_excel()

你可能感兴趣的:(python)