最近一段時間,老板給我的任務(wù)是發(fā)票的結(jié)構(gòu)化識別焊虏。目前市面上成熟的結(jié)構(gòu)化文本識別已經(jīng)很多了港令,關(guān)于如何才能夠?qū)ψR別結(jié)果進行結(jié)構(gòu)化分析這個問題啥容,困擾了我已經(jīng)有段時間了锈颗。像百度OCR應(yīng)該是知名度數(shù)一數(shù)二的,它能提供幾乎所有的結(jié)構(gòu)化文本識別服務(wù)咪惠,而我很長時間只停留在識別圖片中的文字击吱,而不能對它進行結(jié)構(gòu)化分析,這種感覺也太難受了遥昧。直到昨天晚上覆醇,說來也奇怪,無緣無故失眠了炭臭,我就想:既然睡不著永脓,那就不如想想明天要干些什么,想到老板最近派的任務(wù)鞋仍,又想到這兩天看過的正則表達式常摧。那這正則表達式不就正是為了處理識別結(jié)果這種字符串的嘛。正好威创,現(xiàn)學(xué)現(xiàn)賣虽界,敲一波,供日后復(fù)習(xí)用嵌赠。
關(guān)鍵字段
1.發(fā)票代碼
2.發(fā)票號碼
3.開票日期
4.校驗碼
5.購買方信息
6.銷售方信息
7.價稅合計
對應(yīng)的正則表達式
1.發(fā)票代碼
(?:(?<!\d)\d{12}(?!\d))
2.發(fā)票號碼
(?:(?<!\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:
"""
增值稅機打發(fā)票結(jié)構(gòu)化識別
"""
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() # 發(fā)票代碼
self.number() # 發(fā)票號碼
self.date() # 開票日期
self.check_code() # 校驗碼
self.total_price() # 價稅合計(小寫)
self.purchaser() # 購買方
self.seller() # 銷售方
def code(self):
"""
發(fā)票代碼識別
"""
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['發(fā)票代碼'] = res1[0]
self.res.update(No)
break
def number(self):
"""
識別發(fā)票號碼
"""
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["發(fā)票號碼"] = 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