之前写过一版 文本摘要提取,但那版并不完美。有所缺陷(但也获得几十次收藏)。
中文文本摘要提取 (文本摘要提取 有代码)基于python
今天写改进版的文本摘要提取。
文本摘要旨在将文本或文本集合转换为包含关键信息的简短摘要。文本摘要按照输入类型可分为单文档摘要和多文档摘要。单文档摘要从给定的一个文档中生成摘要,多文档摘要从给定的一组主题相关的文档中生成摘要。按照输出类型可分为抽取式摘要和生成式摘要。
摘要:意思就是从一段文本 用几句话来概括这段话的意思
范例文本
花果园中央商务区F4栋楼B33城,时尚星廊,市民来电反映在此店消费被欺骗,该店承诺消费不用钱但后来需要收钱,并且此处工作人员表示自己是著名发型师,市民表示向花果园工商局反映后工作人员不作为,并且自己有相关证据,但花果园工商局叫其自己进行协商,市民希望职能部门能够尽快处理此问题,对此店的行为进行处罚并且将自己的钱退还,请职能部门及时处理。商家联系方式:11111111111。
{‘mean_scoredsenteces’: [‘花果园中央商务区F4栋楼B33城,’, ‘市民来电反映在此店消费被欺骗,’, ‘该店承诺消费不用钱但后来需要收钱,’, ‘市民表示向花果园工商局反映后工作人员不作为,’]}
{‘topnsenteces’: [‘花果园中央商务区F4栋楼B33城,’, ‘该店承诺消费不用钱但后来需要收钱,’, ‘市民表示向花果园工商局反映后工作人员不作为,’]}
第一步:分句
#coding:utf-8
texts="""花果园中央商务区F4栋楼B33城,时尚星廊,市民来电反映在此店消费被欺骗,该店承诺消费不用钱但后来需要收钱,并且此处工作人员表示自己是著名发型师,市民表示向花果园工商局反映后工作人员不作为,并且自己有相关证据,但花果园工商局叫其自己进行协商,市民希望职能部门能够尽快处理此问题,对此店的行为进行处罚并且将自己的钱退还,请职能部门及时处理。商家联系方式:11111111111。"""
def sent_tokenizer(texts):
start=0
i=0#每个字符的位置
sentences=[]
punt_list=',.!?:;~,。!?:;~'#标点符号
for text in texts:#遍历每一个字符
if text in punt_list and token not in punt_list: #检查标点符号下一个字符是否还是标点
sentences.append(texts[start:i+1])#当前标点符号位置
start=i+1#start标记到下一句的开头
i+=1
else:
i+=1#若不是标点符号,则字符位置继续前移
token=list(texts[start:i+2]).pop()#取下一个字符.pop是删除最后一个
if start<len(texts):
sentences.append(texts[start:])#这是为了处理文本末尾没有标点符号的情况
return sentences
sentence=sent_tokenizer(str(texts))
print(sentence)
第二步:加载停用词
停用词:见nlp 中文停用词数据集
#停用词
def stopwordslist(filepath):
stopwords = [line.strip() for line in open(filepath, 'r', encoding='gbk').readlines()]
return stopwords
# 加载停用词
stopwords = stopwordslist("停用词.txt")
第三步:取出高频词
我这里取出的前20个高频词
针对不同长度的文本可以取更多的高频词,文本越长,需要的高频词越多
import nltk
import jieba
import numpy
stopwords = stopwordslist("停用词.txt")
sentence=sent_tokenizer(texts)#分句
words=[w for sentence in sentence for w in jieba.cut(sentence) if w not in stopwords if len(w)>1 and w!='\t']#词语,非单词词,同时非符号
wordfre=nltk.FreqDist(words)#统计词频
topn_words=[w[0] for w in sorted(wordfre.items(),key=lambda d:d[1],reverse=True)][:20]#取出词频最高的20个单词
第四步:核心代码:给句子打分
思路
def _score_sentences(sentences,topn_words):#参数 sentences:文本组(分好句的文本,topn_words:高频词组
scores=[]
sentence_idx=-1#初始句子索引标号-1
for s in [list(jieba.cut(s)) for s in sentences]:# 遍历每一个分句,这里的每个分句是 分词数组 分句1类似 ['花', '果园', '中央商务区', 'F4', '栋楼', 'B33', '城', ',']
sentence_idx+=1 #句子索引+1。。0表示第一个句子
word_idx=[]#存放高频词在分句中的索引位置.得到结果类似:[1, 2, 3, 4, 5],[0, 1],[0, 1, 2, 4, 5, 7]..
for w in topn_words:#遍历每一个高频词
try:
word_idx.append(s.index(w))#高频词出现在该分句子中的索引位置
except ValueError:#w不在句子中
pass
word_idx.sort()
if len(word_idx)==0:
continue
#对于两个连续的单词,利用单词位置索引,通过距离阀值计算族
clusters=[] #存放的是几个cluster。类似[[0, 1, 2], [4, 5], [7]]
cluster=[word_idx[0]] #存放的是一个类别(簇) 类似[0, 1, 2]
i=1
while i<len(word_idx):#遍历 当前分句中的高频词
CLUSTER_THRESHOLD=2#举例阈值我设为2
if word_idx[i]-word_idx[i-1]<CLUSTER_THRESHOLD:#如果当前高频词索引 与前一个高频词索引相差小于3,
cluster.append(word_idx[i])#则认为是一类
else:
clusters.append(cluster[:])#将当前类别添加进clusters=[]
cluster=[word_idx[i]] #新的类别
i+=1
clusters.append(cluster)
#对每个族打分,每个族类的最大分数是对句子的打分
max_cluster_score=0
for c in clusters:#遍历每一个簇
significant_words_in_cluster=len(c)#当前簇 的高频词个数
total_words_in_cluster=c[-1]-c[0]+1#当前簇里 最后一个高频词 与第一个的距离
score=1.0*significant_words_in_cluster*significant_words_in_cluster/total_words_in_cluster
if score>max_cluster_score:
max_cluster_score=score
scores.append((sentence_idx,max_cluster_score))#存放当前分句的最大簇(说明下,一个分解可能有几个簇) 存放格式(分句索引,分解最大簇得分)
return scores;
scored_sentences=_score_sentences(sentence,topn_words)
print(scored_sentences)
第五步:摘要提取
方法1:利用均值和标准差过滤非重要句子
avg=numpy.mean([s[1] for s in scored_sentences])#均值
std=numpy.std([s[1] for s in scored_sentences])#标准差
mean_scored=[(sent_idx,score) for (sent_idx,score) in scored_sentences if score>(avg+0.5*std)]#sent_idx 分句标号,score得分
c= dict(mean_scored_summary=[sentence[idx] for (idx,score) in mean_scored])
print(c)
方法2:返回得分最高的几个句子
top_n_scored=sorted(scored_sentences,key=lambda s:s[1])[-3:]#对得分进行排序,取出3个句子
top_n_scored=sorted(top_n_scored,key=lambda s:s[0])#对得分最高的几个分句,进行分句位置排序
c= dict(top_n_summary=[sentence[idx] for (idx,score) in top_n_scored])
print(c)
第五步:整理后的全文代码
#coding:utf-8
import nltk
import jieba
import numpy
#分句
def sent_tokenizer(texts):
start=0
i=0#每个字符的位置
sentences=[]
punt_list=',.!?:;~,。!?:;~'#标点符号
for text in texts:#遍历每一个字符
if text in punt_list and token not in punt_list: #检查标点符号下一个字符是否还是标点
sentences.append(texts[start:i+1])#当前标点符号位置
start=i+1#start标记到下一句的开头
i+=1
else:
i+=1#若不是标点符号,则字符位置继续前移
token=list(texts[start:i+2]).pop()#取下一个字符.pop是删除最后一个
if start<len(texts):
sentences.append(texts[start:])#这是为了处理文本末尾没有标点符号的情况
return sentences
#对停用词加载
def stopwordslist(filepath):
stopwords = [line.strip() for line in open(filepath, 'r', encoding='gbk').readlines()]
return stopwords
#对句子打分
def score_sentences(sentences,topn_words):#参数 sentences:文本组(分好句的文本,topn_words:高频词组
scores=[]
sentence_idx=-1#初始句子索引标号-1
for s in [list(jieba.cut(s)) for s in sentences]:# 遍历每一个分句,这里的每个分句是 分词数组 分句1类似 ['花', '果园', '中央商务区', 'F4', '栋楼', 'B33', '城', ',']
sentence_idx+=1 #句子索引+1。。0表示第一个句子
word_idx=[]#存放关键词在分句中的索引位置.得到结果类似:[1, 2, 3, 4, 5],[0, 1],[0, 1, 2, 4, 5, 7]..
for w in topn_words:#遍历每一个高频词
try:
word_idx.append(s.index(w))#关键词出现在该分句子中的索引位置
except ValueError:#w不在句子中
pass
word_idx.sort()
if len(word_idx)==0:
continue
#对于两个连续的单词,利用单词位置索引,通过距离阀值计算族
clusters=[] #存放的是几个cluster。类似[[0, 1, 2], [4, 5], [7]]
cluster=[word_idx[0]] #存放的是一个类别(簇) 类似[0, 1, 2]
i=1
while i<len(word_idx):#遍历 当前分句中的高频词
CLUSTER_THRESHOLD=2#举例阈值我设为2
if word_idx[i]-word_idx[i-1]<CLUSTER_THRESHOLD:#如果当前高频词索引 与前一个高频词索引相差小于3,
cluster.append(word_idx[i])#则认为是一类
else:
clusters.append(cluster[:])#将当前类别添加进clusters=[]
cluster=[word_idx[i]] #新的类别
i+=1
clusters.append(cluster)
#对每个族打分,每个族类的最大分数是对句子的打分
max_cluster_score=0
for c in clusters:#遍历每一个簇
significant_words_in_cluster=len(c)#当前簇 的高频词个数
total_words_in_cluster=c[-1]-c[0]+1#当前簇里 最后一个高频词 与第一个的距离
score=1.0*significant_words_in_cluster*significant_words_in_cluster/total_words_in_cluster
if score>max_cluster_score:
max_cluster_score=score
scores.append((sentence_idx,max_cluster_score))#存放当前分句的最大簇(说明下,一个分解可能有几个簇) 存放格式(分句索引,分解最大簇得分)
return scores;
#结果输出
def results(texts,topn_wordnum,n):#texts 文本,topn_wordnum高频词个数,为返回几个句子
stopwords = stopwordslist("停用词.txt")#加载停用词
sentence = sent_tokenizer(texts) # 分句
words = [w for sentence in sentence for w in jieba.cut(sentence) if w not in stopwords if
len(w) > 1 and w != '\t'] # 词语,非单词词,同时非符号
wordfre = nltk.FreqDist(words) # 统计词频
topn_words = [w[0] for w in sorted(wordfre.items(), key=lambda d: d[1], reverse=True)][:topn_wordnum] # 取出词频最高的topn_wordnum个单词
scored_sentences = score_sentences(sentence, topn_words)#给分句打分
# 1,利用均值和标准差过滤非重要句子
avg = numpy.mean([s[1] for s in scored_sentences]) # 均值
std = numpy.std([s[1] for s in scored_sentences]) # 标准差
mean_scored = [(sent_idx, score) for (sent_idx, score) in scored_sentences if
score > (avg + 0.5 * std)] # sent_idx 分句标号,score得分
# 2,返回top n句子
top_n_scored = sorted(scored_sentences, key=lambda s: s[1])[-n:] # 对得分进行排序,取出n个句子
top_n_scored = sorted(top_n_scored, key=lambda s: s[0]) # 对得分最高的几个分句,进行分句位置排序
c = dict(mean_scoredsenteces=[sentence[idx] for (idx, score) in mean_scored])
c1=dict(topnsenteces=[sentence[idx] for (idx, score) in top_n_scored])
return c,c1
if __name__=='__main__':
texts = str(input('请输入文本:'))
topn_wordnum=int(input('请输入高频词数:'))
n=int(input('请输入要返回的句子个数:'))
c,c1=results(texts,topn_wordnum,n)
print(c)
print(c1)
封装成简单 界面
超级简单额界面程序,懒得写太复杂的界面
#coding:utf-8
import nltk
import jieba
import numpy
#分句
def sent_tokenizer(texts):
start=0
i=0#每个字符的位置
sentences=[]
punt_list=',.!?:;~,。!?:;~'#标点符号
for text in texts:#遍历每一个字符
if text in punt_list and token not in punt_list: #检查标点符号下一个字符是否还是标点
sentences.append(texts[start:i+1])#当前标点符号位置
start=i+1#start标记到下一句的开头
i+=1
else:
i+=1#若不是标点符号,则字符位置继续前移
token=list(texts[start:i+2]).pop()#取下一个字符.pop是删除最后一个
if start<len(texts):
sentences.append(texts[start:])#这是为了处理文本末尾没有标点符号的情况
return sentences
#对停用词加载
def stopwordslist(filepath):
stopwords = [line.strip() for line in open(filepath, 'r', encoding='gbk').readlines()]
return stopwords
#对句子打分
def score_sentences(sentences,topn_words):#参数 sentences:文本组(分好句的文本,topn_words:高频词组
scores=[]
sentence_idx=-1#初始句子索引标号-1
for s in [list(jieba.cut(s)) for s in sentences]:# 遍历每一个分句,这里的每个分句是 分词数组 分句1类似 ['花', '果园', '中央商务区', 'F4', '栋楼', 'B33', '城', ',']
sentence_idx+=1 #句子索引+1。。0表示第一个句子
word_idx=[]#存放关键词在分句中的索引位置.得到结果类似:[1, 2, 3, 4, 5],[0, 1],[0, 1, 2, 4, 5, 7]..
for w in topn_words:#遍历每一个高频词
try:
word_idx.append(s.index(w))#关键词出现在该分句子中的索引位置
except ValueError:#w不在句子中
pass
word_idx.sort()
if len(word_idx)==0:
continue
#对于两个连续的单词,利用单词位置索引,通过距离阀值计算族
clusters=[] #存放的是几个cluster。类似[[0, 1, 2], [4, 5], [7]]
cluster=[word_idx[0]] #存放的是一个类别(簇) 类似[0, 1, 2]
i=1
while i<len(word_idx):#遍历 当前分句中的高频词
CLUSTER_THRESHOLD=2#举例阈值我设为2
if word_idx[i]-word_idx[i-1]<CLUSTER_THRESHOLD:#如果当前高频词索引 与前一个高频词索引相差小于3,
cluster.append(word_idx[i])#则认为是一类
else:
clusters.append(cluster[:])#将当前类别添加进clusters=[]
cluster=[word_idx[i]] #新的类别
i+=1
clusters.append(cluster)
#对每个族打分,每个族类的最大分数是对句子的打分
max_cluster_score=0
for c in clusters:#遍历每一个簇
significant_words_in_cluster=len(c)#当前簇 的高频词个数
total_words_in_cluster=c[-1]-c[0]+1#当前簇里 最后一个高频词 与第一个的距离
score=1.0*significant_words_in_cluster*significant_words_in_cluster/total_words_in_cluster
if score>max_cluster_score:
max_cluster_score=score
scores.append((sentence_idx,max_cluster_score))#存放当前分句的最大簇(说明下,一个分解可能有几个簇) 存放格式(分句索引,分解最大簇得分)
return scores;
#结果输出
def results(texts,topn_wordnum,n):#texts 文本,topn_wordnum高频词个数,为返回几个句子
stopwords = stopwordslist("停用词.txt")#加载停用词
sentence = sent_tokenizer(texts) # 分句
words = [w for sentence in sentence for w in jieba.cut(sentence) if w not in stopwords if
len(w) > 1 and w != '\t'] # 词语,非单词词,同时非符号
wordfre = nltk.FreqDist(words) # 统计词频
topn_words = [w[0] for w in sorted(wordfre.items(), key=lambda d: d[1], reverse=True)][:topn_wordnum] # 取出词频最高的topn_wordnum个单词
scored_sentences = score_sentences(sentence, topn_words)#给分句打分
# 1,利用均值和标准差过滤非重要句子
avg = numpy.mean([s[1] for s in scored_sentences]) # 均值
std = numpy.std([s[1] for s in scored_sentences]) # 标准差
mean_scored = [(sent_idx, score) for (sent_idx, score) in scored_sentences if
score > (avg + 0.5 * std)] # sent_idx 分句标号,score得分
# 2,返回top n句子
top_n_scored = sorted(scored_sentences, key=lambda s: s[1])[-n:] # 对得分进行排序,取出n个句子
top_n_scored = sorted(top_n_scored, key=lambda s: s[0]) # 对得分最高的几个分句,进行分句位置排序
c = dict(mean_scoredsenteces=[sentence[idx] for (idx, score) in mean_scored])
c1=dict(topnsenteces=[sentence[idx] for (idx, score) in top_n_scored])
return c,c1
from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QVBoxLayout, QPushButton,QLabel,QLineEdit,QFormLayout
import sys
class TextEditDemo(QWidget):
def __init__(self, parent=None):
super(TextEditDemo, self).__init__(parent)
self.setWindowTitle("中文摘要提取")
self.resize(500, 570)
self.label1 = QLabel('输入文本')
self.textEdit1 = QTextEdit()
self.lineedit1 = QLineEdit()#请输入高频词数
self.lineedit2 = QLineEdit()#请输入返回句子数
self.btnPress1 = QPushButton("点击运行")
self.textEdit2 = QTextEdit()#方法1显示
self.textEdit3 = QTextEdit()#方法2 显示
flo = QFormLayout()#表单布局
flo.addRow("请输入高频词数:", self.lineedit1)
flo.addRow("请输入返回句子数:", self.lineedit2)
layout = QVBoxLayout()
layout.addWidget(self.label1)
layout.addWidget(self.textEdit1)
layout.addLayout(flo)
layout.addWidget(self.btnPress1)
layout.addWidget(self.textEdit2)
layout.addWidget(self.textEdit3)
self.setLayout(layout)
self.btnPress1.clicked.connect(self.btnPress1_Clicked)
def btnPress1_Clicked(self):
try:
text = self.textEdit1.toPlainText() # 返回输入的文本
topn_wordnum = int(self.lineedit1.text()) # 高频词 20
n = int(self.lineedit2.text()) # 3个返回句子
c, c1 = results(str(text), topn_wordnum, n)
self.textEdit2.setPlainText(str(c))
self.textEdit2.setStyleSheet("font:10pt '楷体';border-width:5px;border-style: inset;border-color:gray")
self.textEdit3.setPlainText(str(c1))
self.textEdit3.setStyleSheet("font:10pt '楷体';border-width:5px;border-style: inset;border-color:red")
except:
self.textEdit2.setPlainText('操作失误')
self.lineedit1.setText('操作失误,请输入整数')
self.lineedit2.setText('操作失误,请输入整数')
if __name__ == "__main__":
app = QApplication(sys.argv)
win = TextEditDemo()
win.show()
sys.exit(app.exec_())