从文章中提取人物姓名(一)

2023-10-16 15:23:27 浏览数 (1)

背景:

ai浪潮滚滚

为了保住饭碗跟上潮流,所以我有兴趣,刚好工作需要这个。于是就来研究一下——从文章中提取专有名词。

那么我们先从简单的来,提取中文人名入手

本文使用python编写

如何提取人名

首先,我们要从文章内提取人名。因为人名大多是百家姓开头,所以可以根据这个筛选一下。

第一步,把文章充分的切成一个个小片段,一个词都不要漏

我吭哧吭哧的写了一个方法,来完成把内容切割为多个min_n到max_n长度的字符串的任务

代码语言:javascript复制
def cut_by_ngram(sentence, min_n, max_n):
    rst = []
    # 遍历切分长度的范围
    for length in range(min_n, min(len(sentence), max_n)   1):
        # 按照此时切分长度进行切分
        for idx in range(0, len(sentence) - length   1):
            rst.append(sentence[idx: idx   length])
    return rst

因为中文名字大多2到4个字,所以我们可以这样调用

代码语言:javascript复制
cut_by_ngram('测试一下'', 2, 4)

但是这样得到的名字,可能存在,但不一定是名字。还可能包含了标点符号,为了更好的观察,我们需要进一步过滤和统计出现次数

首先判断是否全是中文

判断是否中文,我们可以用Unicode的大小判断

代码语言:javascript复制
def is_chinese(char):
    if 'u4e00' <= char <= 'u9fff':
        return True
    else:
        return False

人名整个是不是由中文组成,我们可以for循环判断,不是就中断方法好了

代码语言:javascript复制
    if len(name) == 1:
      return
    for x in name:
        if is_chinese(x) is False:
            # print('not is_chinese')
            return

然后我们还需要统计一下出现的次数

完整代码如下

代码语言:javascript复制
# 通过这个方法,过滤名字,并计算出现次数

def getNameObj(slice_word):
    name_obj = {}
    # 通过这个方法,过滤名字,并计算出现次数
    def filterName(name):
        if len(name) == 1:
            return
        for x in name:
            if is_chinese(x) is False:
                # print('not is_chinese')
                return
        num = last_names[name[0]]
        # print('filterName:' name)
        if num is None or math.isinf(num):
            # print('error:',name)
            pass
        else:
            if name in name_obj:
                name_obj[name] = name_obj[name]   1
            else:
                name_obj[name] = 1
            return name
    content2_arr = list(filter(filterName, slice_word))
    # print(content2_arr)
    return name_obj

既然我们计算了次数,那么我们就要用上,比如给他排序

代码语言:javascript复制
name_obj = {"李相":1,"李亮":2}
def sort_name(name):
    return name_obj[name]
names = sorted(list(name_obj), key=sort_name, reverse=True )
sort_sign_names =  []
for index,item in enumerate(names, 0):
    sort_sign_names.append( item   '-'   str(name_obj[item]))

最后输出,["李亮","李相":1]

最后,贴一下完整的代码,如下

代码语言:javascript复制
import math
# 把内容切割为多个min_n到max_n长度的字符串
def cut_by_ngram(sentence, min_n, max_n):
    rst = []
    # 遍历切分长度的范围
    for length in range(min_n, min(len(sentence), max_n)   1):
        # 按照此时切分长度进行切分
        for idx in range(0, len(sentence) - length   1):
            rst.append(sentence[idx: idx   length])
    return rst
# 方便排序,如果没有值的情况下返回无穷大
class my_dict(dict):
    def __missing__(self, key):
        self[key] = float("Inf")
        return self[key]
# 百家姓
content = '赵 钱 孙 李 周 吴 郑 王'
# 读取百家姓文件,这个文件里面的姓氏
# file = open('./baijiaxing.txt',mode='r',encoding='utf-8')
# content = file.read()
last_name_arr = content.replace('n', ' ').replace('  ', ' ').split(' ')
# last_name_arr = content.replace('n', ' ').replace('  ', ' ').split(' ')[0:100]
# 生成对象 格式为{"赵":1,"钱":2}
# 1.方便后续排序(没有用上这个功能)
# 2.数组转对象,提升匹配速度
last_name_obj = {}
for index,last_name in enumerate(last_name_arr, 1):
    last_name_obj[last_name]=index
last_names = my_dict(last_name_obj)
# print(last_names)
def is_chinese(char):
    if 'u4e00' <= char <= 'u9fff':
        return True
    else:
        return False
def getNameObj(slice_word):
    name_obj = {}
    # 通过这个方法,过滤名字,并计算出现次数
    def filterName(name):
        if len(name) == 1:
            return
        for x in name:
            if is_chinese(x) is False:
                # print('not is_chinese')
                return
        num = last_names[name[0]]
        # print('filterName:' name)
        if num is None or math.isinf(num):
            # print('error:',name)
            pass
        else:
            if name in name_obj:
                name_obj[name] = name_obj[name]   1
            else:
                name_obj[name] = 1
            return name
    content2_arr = list(filter(filterName, slice_word))
    # print(content2_arr)
    return name_obj
# 读取文章
# file2 = open('./名人传记62本(合集)_001.txt',mode='r',encoding='utf-8')
# content2 = file2.read()
# slice_word = cut_by_ngram(content2,2,4)
slice_word = cut_by_ngram('我喜欢李相,李亮,李亮',2,3)
name_obj = getNameObj(slice_word)
# print(slice_word)
# print(name_obj)
def sort_name(name):
    return name_obj[name]
names = sorted(list(name_obj), key=sort_name, reverse=True )
sort_sign_names =  []
for index,item in enumerate(names, 0):
    sort_sign_names.append( item   '-'   str(name_obj[item]))
# 按照名字出现次数的高低排序,出现次数越多越是考前
print(sort_sign_names)
# 把结果写入文件
file=open('data.json',mode='w',encoding='utf-8')
file.write(str(sort_sign_names));
file.close()  

但是,大家会发现这样依然有一些问题,比如有些人名符合规则,但是可能不是人名,是专有名词,比如 高考,云南等。

大家可以尝试自己优化,我这边也会接着出新的文章来完善这一主题。

0 人点赞