最近一段时间,老板给我的任务是发票的结构化识别。目前市面上成熟的结构化文本识别已经很多了,关于如何才能够对识别结果进行结构化分析这个问题,困扰了我已经有段时间了。像百度OCR应该是知名度数一数二的,它能提供几乎所有的结构化文本识别服务,而我很长时间只停留在识别图片中的文字,而不能对它进行结构化分析,这种感觉也太难受了。直到昨天晚上,说来也奇怪,无缘无故失眠了,我就想:既然睡不着,那就不如想想明天要干些什么,想到老板最近派的任务,又想到这两天看过的正则表达式。那这正则表达式不就正是为了处理识别结果这种字符串的嘛。正好,现学现卖,敲一波,供日后复习用。
关键字段
1.发票代码
2.发票号码
3.开票日期
4.校验码
5.购买方信息
6.销售方信息
7.价税合计
对应的正则表达式
1.发票代码
(?:(?<!\d)\d{12}(?!\d))
2.发票号码
(?:(?<!\d)\d{8}(?!\d))
3.开票日期
[0-9]{1,4}年[0-9]{1,2}月[0-9]{1,2}
4.校验码
(?:校验码:|校验码:)[0-9]{1,20}
5.购买方信息
名称 (?:称:|称:)[\u4e00-\u9fa5]+
纳税人识别号 (?:纳税人识别号:|纳税人识别号:)\w+
地址、电话 (?:地址、电话:|地址、电话:)[\-0-9\u4e00-\u9fa5]+
开户行及账号 (?:开户行及账号:|开户行及账号:)[0-9\u4e00-\u9fa5]+
6.销售方信息
名称 (?:称:|称:)[\u4e00-\u9fa5]+
纳税人识别号 (?:纳税人识别号:|纳税人识别号:)\w+
地址、电话 (?:地址、电话:|地址、电话:)[\-0-9\u4e00-\u9fa5]+
开户行及账号 (?:开户行及账号:|开户行及账号:)[0-9\u4e00-\u9fa5]+
7.价税合计
[零壹贰叁肆伍陆柒捌玖圆整角]+
完整代码
import re
class invoice_m:
"""
增值税机打发票结构化识别
"""
def __init__(self, result):
self.FLAG_NAME = True
self.FLAG_NO = True
self.FLAG_ADD_TEL = True
self.FLAG_BANK_NO = True
self.result = result
self.N = len(self.result)
self.res = {}
self.code() # 发票代码
self.number() # 发票号码
self.date() # 开票日期
self.check_code() # 校验码
self.total_price() # 价税合计(小写)
self.purchaser() # 购买方
self.seller() # 销售方
def code(self):
"""
发票代码识别
"""
No = {}
for i in range(self.N):
txt = self.result[i].replace(' ', '')
txt = txt.replace(' ', '')
res1 = re.findall('(?:(?<!\d)\d{10}(?!\d))', txt)
res1 += re.findall('(?:(?<!\d)\d{12}(?!\d))', txt)
if len(res1) > 0:
No['发票代码'] = res1[0]
self.res.update(No)
break
def number(self):
"""
识别发票号码
"""
nu = {}
for i in range(self.N):
txt = self.result[i].replace(' ', '')
txt = txt.replace(' ', '')
res1 = re.findall('(?:(?<!\d)\d{8}(?!\d))', txt)
if len(res1) > 0:
nu["发票号码"] = res1[0]
self.res.update(nu)
break
def date(self):
"""
识别开票日期
"""
da = {}
for i in range(self.N):
txt = self.result[i].replace(' ', '')
txt = txt.replace(' ', '')
res1 = re.findall('[0-9]{1,4}年[0-9]{1,2}月[0-9]{1,2}', txt)
if len(res1) > 0:
da["开票日期"] = res1[0] + '日'
self.res.update(da)
break
def check_code(self):
"""
校验码识别
"""
check = {}
for i in range(self.N):
txt = self.result[i].replace(' ', '')
txt = txt.replace(' ', '')
res = re.findall('校验码[0-9]{1,20}', txt)
res += re.findall('码[0-9]{1,20}', txt)
res += re.findall('(?:校验码:|校验码:)[0-9]{1,20}', txt)
if len(res) > 0:
check['校验码'] = res[0].replace('校验码', '').replace(':', '').replace('码', '').replace(':', '')
self.res.update(check)
break
def total_price(self):
"""
识别价税合计(小写)
"""
total_pri = {}
switcher = {
"零": '0',
"壹": '1',
"贰": '2',
"叁": '3',
"肆": '4',
"伍": '5',
"陆": '6',
"柒": '7',
"捌": '8',
"玖": '9',
"圆": '.',
"整": '00',
"角": ''
}
for i in range(self.N):
txt = self.result[i].replace(' ', '')
txt = txt.replace(' ', '')
res1 = re.findall('[零壹贰叁肆伍陆柒捌玖圆整角]+', txt)
res2 = ''
if len(res1) > 0:
for j in range(len(res1)):
if len(res1[j]) > 1:
for k in range(len(res1[j])):
res2 += switcher[res1[j][k]]
if res1[j][-1] == "角":
res2 += '0'
else:
res2 += switcher[res1[j]]
if res1[-1] == "角":
res2 += '0'
total_pri["价税合计"] = '¥' + res2
self.res.update(total_pri)
break
def purchaser(self):
"""
购买方信息识别
"""
purchaser_info = {}
self.res.setdefault('购买方')
for i in range(self.N):
txt = self.result[i].replace(' ', '')
txt = txt.replace(' ', '')
name = re.findall('(?:称:|称:)[\u4e00-\u9fa5]+', txt)
no = re.findall('(?:纳税人识别号:|纳税人识别号:)\w+', txt)
add_tel = re.findall('(?:地址、电话:|地址、电话:)[\-0-9\u4e00-\u9fa5]+', txt)
bank_no = re.findall('(?:开户行及账号:|开户行及账号:)[0-9\u4e00-\u9fa5]+', txt)
if len(name) > 0 and self.FLAG_NAME:
self.FLAG_NAME = False
purchaser_info['名称'] = name[0].replace(':', '').replace(':', '').replace('称', '')
if len(no) > 0 and self.FLAG_NO:
self.FLAG_NO = False
purchaser_info['纳税人识别号'] = no[0].replace(':', '').replace(':', '').replace('纳税人识别号', '')
if len(add_tel) > 0 and self.FLAG_ADD_TEL:
self.FLAG_ADD_TEL = False
purchaser_info['地址、电话'] = add_tel[0].replace(':', '').replace(':', '').replace('地址、电话', '')
if len(bank_no) > 0 and self.FLAG_BANK_NO:
self.FLAG_BANK_NO = False
purchaser_info['开户行及账号'] = bank_no[0].replace(':', '').replace(':', '').replace('开户行及账号', '')
self.res['购买方'] = purchaser_info
def seller(self):
"""
销售方信息识别
"""
seller_info = {}
self.res.setdefault('销售方')
for i in range(self.N):
txt = self.result[i].replace(' ', '')
txt = txt.replace(' ', '')
name = re.findall('(?:称:|称:)[\u4e00-\u9fa5]+', txt)
no = re.findall('(?:纳税人识别号:|纳税人识别号:)\w+', txt)
add_tel = re.findall('(?:地址、电话:|地址、电话:)[\-0-9\u4e00-\u9fa5]+', txt)
bank_no = re.findall('(?:开户行及账号:|开户行及账号:)[0-9\u4e00-\u9fa5]+', txt)
if len(name) > 0 and not self.FLAG_NAME:
seller_info['名称'] = name[0].replace(':', '').replace(':', '').replace('称', '')
if len(no) > 0 and not self.FLAG_NO:
seller_info['纳税人识别号'] = no[0].replace(':', '').replace(':', '').replace('纳税人识别号', '')
if len(add_tel) > 0 and not self.FLAG_ADD_TEL:
seller_info['地址、电话'] = add_tel[0].replace(':', '').replace(':', '').replace('地址、电话', '')
if len(bank_no) > 0 and not self.FLAG_BANK_NO:
seller_info['开户行及账号'] = bank_no[0].replace(':', '').replace(':', '').replace('开户行及账号', '')
self.res['销售方'] = seller_info
网友评论