美文网首页
发票结构化识别

发票结构化识别

作者: 湯木 | 来源:发表于2020-09-09 15:02 被阅读0次

    最近一段时间,老板给我的任务是发票的结构化识别。目前市面上成熟的结构化文本识别已经很多了,关于如何才能够对识别结果进行结构化分析这个问题,困扰了我已经有段时间了。像百度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
    

    相关文章

      网友评论

          本文标题:发票结构化识别

          本文链接:https://www.haomeiwen.com/subject/sgpeektx.html