基于TF-IDF算法提取文章关键词设计

测试数据是来自于搜狗实验室的新闻数据

下载地址为:

https://www.sogou.com/labs/resource/cs.php

大致流程为:

  1. 读取测试样本文件;
  2. 读入停用词表,并对合并后的文本进行分词、去停用词和特殊符号;
  3. 遍历处理过后的文本,将其存入文档中;
  4. 进行词频统计,对所有词语出现的次数进行统计;
  5. 计算词频TF,统计含有该单词的句子数;
  6. 计算逆文档频率IDF,计算TF-IDF;
  7. 存储全部的关键词,排序得到降序的列表;
  8. 定义文章返回关键词的数量,将结果保存到文件。
# -*- coding:utf-8 -*-
# 因为py文件不支持中文(包括注释里的),因此需要使用这段代码将就文件编码类型改为UTF-8的类型。
# 这样就可以在py文件中正常输入中文而不报错了
from lxml import etree

# 导入 jieba
import jieba  #中文分词
import math   #计算IDF时会用到log函数
'''
defaultdict 用法与 dict 相似,在dict中取key的值,若key不在dict中,报错;
若用defaultdict,则不会报错,返回值是工厂函数默认参数值
(工厂函数是list,返回[]。工厂函数是str,返回空字符串’’。工厂函数是set,返回set()。)
'''
from collections import defaultdict
'''
 Zhon是一个Python库,它提供了中文文本处理中常用的常量。
python自带punctuation包,可以消除所有中文标点符号
''' 
from zhon.hanzi import punctuation
# string.punctuation将给出所有的标点符号。
from string import punctuation as eng_punctuation
import time


def parse_xml(xml):
    """解析xml文件"""
    try:
        # 获取文本
        '''
        etree.HTML()可以用来解析字符串格式的HTML文档对象,将传进去的字符串转变成_Element对象。
        作为_Element对象,可以方便的使用getparent()、remove()、xpath()等方法。
        如果想通过xpath获取html源码中的内容,就要先将html源码转换成_Element对象,然后再使用xpath()方法进行解析。
        '''
        dom_tree = etree.HTML(xml)  #将xml字符串转变成_Element对象
        title = ''.join(dom_tree.xpath('//doc/contenttitle/text()')) # 提取标题
        content = ''.join(dom_tree.xpath('//doc/content/text()')) # 提取文本
        # data = {
        #     # 'url': url,
        #     # 'docno': docno,
        #     'title': title.replace('\u3000', ' '),
        #     'content': content.replace('\u3000', ' ').replace('\ue40c', '  '),            
        # }
        # print(data)    #打印文件内容
        for p in all_punctuation: # 去掉标点符号和特殊符号
            title.replace(p,' ')  #将title中标点符号和特殊符号替换成' '
            content.replace(p,' ')#将content中标点符号和特殊符号替换成' '
        """
        1、精确模式,试图将句子精确的分开,适用于文本分析。
        2、全模式,把句子中所有可以成词的词语都扫描出来,速度非常快,但是不能解决歧义。
        3、搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适用于搜索引擎分词。
        """
        # 精确模式进行分词
        seg_list = jieba.cut(title, cut_all=False)  # title 进行分词
        seg_list1 = jieba.cut(content, cut_all=False)  # content 进行分词
        all_word_list = list() # 存储分词并去除停用词和特殊符号后的词语
        for word in list(seg_list)+list(seg_list1): # 两个一起过滤
        # Python教材中说基本汉字的Unicode编码范围是0x4e00~0x9fa5
        # Unicode 14.0把4E00-9FFF全部定义完了,9FA6-9FFF是否显示取决于当前使用的字体是否设计了这些编码的字形。
        # stopword_list:停用词
            if word not in stopword_list and '\u4e00' <= word <= '\u9fff' and len(word) >= 2: # 过滤停用词
                all_word_list.append(word)  #将过滤后的词语添加到all_word_list中
        return all_word_list

    except Exception as e: #忽略异常Exception(e)并执行except的子句
        print('xml-->', xml)
        raise Exception(e) #使用raise 语句抛出一个指定的异常Exception(e)

def parse_dat(top_n): #关键词的数量参数:top_n
    all_word_list = list()   #存储的分词
    j = 1
    now_start_time = time.time()  #time.time():计算代码运行时间,这里开始计时
    with open('news_sohusite_xml.dat', 'r+', encoding='ansi') as f:  #用读写方式打开文件
        while j <= 50000:  # 定义读取文件中数据的个数
            xml = ''
            start_time = time.time()
            for i in range(6):  #通过测试数据地址知数据源为6行
                line = f.readline()   #readline:逐行读取
                xml += line.replace('\n', '')  #'\n'替换为 ''
            if xml:
                word_list = parse_xml(xml)   #调用parse_xml(xml)解析xml文件
                end_time = time.time()
                if j % 100 == 0:
                    print('已经处理了', j, '个html数据,耗时', end_time-start_time,'总消耗时间',end_time-now_start_time)  # 输出已经处理了多少个html
                j += 1
            else:
                break
            all_word_list.append(word_list)  #将解析后的word_list添加到all_word_list中
    with open('all_cut_word_list.txt','w', encoding='utf-8') as f: 
        for word_list in all_word_list:  #all_word_list中的word_list写入到all_cut_word_list.txt
            f.write(str(word_list)+'\n') 
    word_count = Counter(all_word_list)  # 调用Counter进行词频统计
    print('词频统计结束,开始进行关键词提取:')
    keyword_list = list() # 存储全部的关键词keyword
    for word_dict in word_count:
        word_tfidf_dict = dict()  # 存放词语->tfidf值的dict
        for word, cnt in word_dict.items():
            tfidf_value = tfidf(word, word_dict, word_count)
            word_tfidf_dict[word] = tfidf_value  # 计算词语的tfidf值并存放到dict中
        sub_keyword = list()
        # 排序得到降序的列表
        # items() 函数以列表返回可遍历的(键, 值) 元组数组
        # sorted(iterable, key=None, reverse=False)
        # 其中,iterable 表示指定的序列,key 参数可以自定义排序规则;
        # reverse 参数指定以升序(False,默认)还是降序(True)进行排序。
        # sorted() 函数会返回一个排好序的列表。
        for r in sorted(word_tfidf_dict.items(), key=lambda x: x[1], reverse=True)[:top_n]:
            sub_keyword.append(r[0])            # r:(词语,TF*IDF值)
        keyword_list.append(sub_keyword)        #将关键词存放到keyword_list
        if len(keyword_list) % 100 == 0:
            print('已完成',len(keyword_list),'个html文本的关键词提取')
    print('关键词已提取完成,请前往result_.txt文本文档查看结果!')
    return keyword_list


# 读取停用词
stopword_list = list([''])
with open('stopword.txt', 'r', encoding='utf-8') as f:
    for r in f.readlines():
        stopword_list.append(r.replace('\n',''))  #'\n'替换为 ''后添加到stopword_list

all_punctuation = list(punctuation)+list(eng_punctuation) #标点符号


# words:对所有句子分词后的结果,[[], ...]
# word_count:对每个句子进行词频统计, [{}, ...]
# word_dict:每一个句子的词频统计结果, {}


# 进行词频统计
def Counter(words):
    word_count = []  #存放word_dict
    for sentence in words:
        word_dict = defaultdict(int)  #防止在dict中取key的值时,若key不在dict中,报错
        for word in sentence:
            word_dict[word] +=1  
        word_count.append(word_dict)  #将word_dict添加到word_count中
    return word_count

# 计算词频TF(word代表被计算的单词,word_dict是被计算单词所在句子分词统计词频后的字典)
def tf(word, word_dict):
    #在某一类中词条w出现的次数/该类中所有的词条数目  
    return word_dict[word] / sum(word_dict.values()) 

# 统计含有该单词的句子数
def count_sentence(word, word_count):
    for i in word_count:
        if i.get(word):      # i[word] >= 1,在i中搜寻word对应的值
            1
    return sum([1])  #即含有该单词的句子数

# 计算逆文档频率IDF:log(文档总数/(包含词条a的文档数+1))
def idf(word, word_count):
    return math.log(len(word_count) / (count_sentence(word, word_count) + 1))

# 计算TF-IDF:TF−IDF=TF∗IDF
def tfidf(word, word_dict, word_count):
    return tf(word, word_dict) * idf(word, word_count)




#直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。
if __name__ == "__main__":   
    top_n = 5     # 定义返回关键词的数量
    html_keyword_list = parse_dat(top_n)  #调用parse_dat(top_n) 
    # len_h = len(html_keyword_list)
    with open('result_.txt', 'w', encoding='utf-8') as f:
        for i in range(len(html_keyword_list)):
            f.write(str(html_keyword_list[i])+'\n') # 将提取的关键词写入result_.txt

运行成功以后的截图如下:

基于TF-IDF算法提取文章关键词设计_第1张图片

 这是部分提取出来的关键词的截图:

基于TF-IDF算法提取文章关键词设计_第2张图片

如有侵权,请联系我。

你可能感兴趣的:(算法,python)