日期实体提取
1 需求
根据用户的每句语音进行解析,识别出用户的酒店预订需求,如房间型号、入住时间等;用户的语音在发送给后台进行请求时已经转换成中文文本,然而由于语音转换工具的识别问题,许多日期类的数据并不是严格的数字,会出现诸如“六月12”“2016年八月”“20160812”“后天下午”等形式。
例如“我要今天住到明天”(假设今天为2017年10月1号),那么通过日期解析后,应该输出为“2017-10-01”和“2017-10-02”。
2 代码
我们主要通过正则表达式和Jieba分词来完成该任务,主要引入以下库:
import re
from datetime import datetime, timedelta
from dateutil.parser import parse
import jieba.posseg as psg
check_time_valid函数,用来对提取的拼接日期串进行进一步处理,以进行有效性判断的。
def check_time_valid(word):
# 对提取的拼接日期串进行进一步处理,以进行有效性判断
m = re.match("\d+$", word)
if m:
if len(word) <= 6:
return None
word1 = re.sub('[号|日]\d+$', '日', word)
if word1 != word:
return check_time_valid(word1)
else:
return word1
parse_datetime在解析具体几个维度时,用了year2dig和cn2dig方法。主要是通过预定义一些模板,将具体的文本转换成相应的数字,以供parse_datetime进行封装。
UTIL_CN_NUM = { '零': 0, '一': 1, '二': 2, '两': 2, '三': 3, '四': 4,
'五': 5, '六': 6, '七': 7, '八': 8, '九': 9,
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
'5': 5, '6': 6, '7': 7, '8': 8, '9': 9
}
UTIL_CN_UNIT = { '十': 10, '百': 100, '千': 1000, '万': 10000}
def cn2dig(src):
if src == "":
return None
m = re.match("\d+", src)
if m:
return int(m.group(0))
rsl = 0
unit = 1
for item in src[::-1]:
if item in UTIL_CN_UNIT.keys():
unit = UTIL_CN_UNIT[item]
elif item in UTIL_CN_NUM.keys():
num = UTIL_CN_NUM[item]
rsl += num * unit
else:
return None
if rsl < unit:
rsl += unit
return rsl
def year2dig(year):
res = ''
for item in year:
if item in UTIL_CN_NUM.keys():
res = res + str(UTIL_CN_NUM[item])
else:
res = res + item
m = re.match("\d+", res)
if m:
if len(m.group(0)) == 2:
return int(datetime.datetime.today().year/100)*100 + int(m.group(0))
else:
return int(m.group(0))
else:
return None
parse_datetime函数,用以将每个提取到的文本日期串进行时间转换。其主要通过正则表达式将日期串进行切割,分为“年”“月”“日”“时”“分”“秒”等具体维度,然后针对每个子维度单独再进行识别。
def parse_datetime(msg):
# 将每个提取到的文本日期串进行时间转换。
# print("parse_datetime开始处理:",msg)
if msg is None or len(msg) == 0:
return None
try:
msg = re.sub("年"," ",msg)# parse不认识"年"字
dt = parse(msg, yearfirst=True, fuzzy=True)
# print(dt)
return dt.strftime('%Y-%m-%d %H:%M:%S')
except Exception as e:
m = re.match(r"([0-9零一二两三四五六七八九十]+年)?([0-9一二两三四五六七八九十]+月)?([0-9一二两三四五六七八九十]+[号日])?([上中下午晚早]+)?([0-9零一二两三四五六七八九十百]+[点:\.时])?([0-9零一二三四五六七八九十百]+分?)?([0-9零一二三四五六七八九十百]+秒)?",
msg)
if m.group(0) is not None:
res = {
"year": m.group(1),
"month": m.group(2),
"day": m.group(3),
"hour": m.group(5) if m.group(5) is not None else '00',
"minute": m.group(6) if m.group(6) is not None else '00',
"second": m.group(7) if m.group(7) is not None else '00',
}
params = { }
for name in res:
if res[name] is not None and len(res[name]) != 0:
tmp = None
if name == 'year':
tmp = year2dig(res[name][:-1])
else:
tmp = cn2dig(res[name][:-1])
if tmp is not None:
params[name] = int(tmp)
target_date = datetime.today().replace(**params)
is_pm = m.group(4)
if is_pm is not None:
if is_pm == u'下午' or is_pm == u'晚上' or is_pm =='中午':
hour = target_date.time().hour
if hour < 12:
target_date = target_date.replace(hour=hour + 12)
return target_date.strftime('%Y-%m-%d %H:%M:%S')
else:
return None
首先通过Jieba分词将带有时间信息的词进行切分,然后记录连续时间信息的词。这里面就用到Jieba词性标注的功能,提取其中“m”(数字)“t”(时间)词性的词。
time_extract实现了这样的规则约束:对句子进行解析,提取其中所有能表示日期时间的词,并进行上下文拼接,如词性标注完后出现“今天/t 住/v 到/v 明天/t 下午/t 3/m点/m”,那么需要将“今天”和“明天下午3点”提取出来。代码里面定义了几个关键日期——“今天”“明天”和“后天”,当解析遇到这些词时进行日期格式转换,以方便后面的解析。
def time_extract(text):
time_res = []
word = ''
keyDate = { '今天': 0, '至今': 0, '明天':1, '后天': 2}
for k, v in psg.cut(text):
if k in keyDate:
if word != '':
time_res.append(word)
word = (datetime.today() + timedelta(days=keyDate.get(k, 0))).strftime('%Y年%m月%d日')
elif word != '':
if v in ['m', 't']:
word = word + k
else:
time_res.append(word)
word = ''
elif v in ['m', 't']:
word = k
if word != '':
time_res.append(word)
# print('22222222',time_res)
result = list(filter(lambda x: x is not None, [check_time_valid(w) for w in time_res]))
final_res = [parse_datetime(w) for w in result]
return [x for x in final_res if x is not None]
识别输入的文本是否直接满足 最后的要求,满足就返回true
def yanZhengRightData(string):
m = re.match("(\d{4}|\d{2})-(\d{2}|\d{1})-(\d{2}|\d{1})", string)
if m:
return True
else:
return time_extract(string)e:
main函数:
if __name__ == '__main__':
print(time_extract("从2016年3月5日至今"))
print(time_extract("在20160824-20180529的全部交易。"))
print(time_extract("2017.6.12-7.10交!"))
print(yanZhengRightData("2017-12-21"))
将上面的代码按顺序拷贝,并粘贴上一下main函数,运行后结果显示为:
['2016-03-05 00:00:00', '2019-12-04 00:00:00']
['2016-08-24 00:00:00', '2018-05-29 00:00:00']
['2017-06-12 00:00:00', '2019-12-07 00:00:00']
True
3 参考:
Python自然语言处理实战:日期实体提取:https://zhuanlan.zhihu.com/p/39088702
原文链接:https://lookme.blog.csdn.net/article/details/103394629
本站声明:网站内容来源于网络,如有侵权,请联系我们,我们将及时处理。
还没有人抢沙发呢~