影刀RPA 发票报销自动化:识别校验与审批流
作者:林焱 | 适用人群:影刀RPA新手 | 难度:★★★☆☆
一、什么情况用这个
财务或者经常报销的同事,每个月要处理几十上百张发票——核对发票信息、查重(防止重复报销)、录入系统、提交审批。一张一张手动处理,眼睛看花还容易出错。
用影刀RPA:读取发票图片/PDF → OCR识别关键字段 → 自动查重 → 写入报销系统 → 提交审批。你只需要把发票丢进文件夹,剩下的机器人搞定。
实际场景:月度费用报销、差旅发票整理、供应商发票校验、发票台账自动维护。
这篇文章能解决的问题:
- OCR识别发票上的关键信息(发票号码、金额、日期、税号)
- 自动查重——防止同一张发票重复报销
- 发票信息自动录入Excel台账
- 对接OA系统提交报销审批
二、怎么做
2.1 环境准备
影刀安装目录\python\python.exe-mpipinstallcnocr PillowCNOCR是专门优化中文的OCR库,对发票上的中文字体识别效果好。
2.2 影刀主流程
┌──────────────────────────────────────────────┐ │ ① 【监控文件夹】 │ │ 监控「待处理发票」文件夹,有新文件触发 │ │ ↓ │ │ ② 【执行Python代码】 │ │ OCR识别发票图片,提取关键信息 │ │ ↓ │ │ ③ 【执行Python代码】 │ │ 发票查重 + 信息校验 │ │ ↓ │ │ ④ 【读取Excel】 │ │ 检查发票台账是否已有此发票 │ │ ↓ │ │ ⑤ 【写入Excel】 │ │ 将校验通过的发票写入台账 │ │ ↓ │ │ ⑥ 【条件判断】 │ │ 需要审批?→ ⑦发送审批请求 │ └──────────────────────────────────────────────┘2.3 OCR识别发票
步骤②:提取发票信息
fromcnocrimportCnOcrimportreimportjsonimportos# 初始化OCR(只需初始化一次)ocr=CnOcr()defextract_invoice_info(image_path):"""从发票图片中提取关键信息"""# OCR识别result=ocr.ocr(image_path)# 拼接所有识别出的文字full_text="\n".join([item['text']foriteminresult])info={"file_path":image_path,"raw_text":full_text,}# ===== 正则提取关键字段 =====# 1. 发票号码(通常8位或10位数字)invoice_no_match=re.search(r'发票号码[::\s]*(\d{8,10})',full_text)ifinvoice_no_match:info["invoice_no"]=invoice_no_match.group(1)# 2. 发票金额(找「合计金额」后面的数字)amount_match=re.search(r'合[计計][金金额][::\s]*[¥¥]?\s*([\d,]+\.?\d{0,2})',full_text)ifamount_match:info["amount"]=amount_match.group(1).replace(",","")# 3. 开票日期date_match=re.search(r'开票日期[::\s]*(\d{4}[-/年]\d{1,2}[-/月]\d{1,2})',full_text)ifdate_match:info["invoice_date"]=date_match.group(1)# 4. 销售方名称seller_match=re.search(r'销售方名[称稱][::\s]*([^\n]{4,30})',full_text)ifseller_match:info["seller_name"]=seller_match.group(1).strip()# 5. 购买方税号(15-20位数字字母组合)tax_id_match=re.search(r'纳税人识别号[::\s]*([A-Za-z0-9]{15,20})',full_text)iftax_id_match:info["tax_id"]=tax_id_match.group(1)returninfo# ===== 批量处理 =====INVOICE_FOLDER=r"D:\发票\待处理"results=[]forfilenameinos.listdir(INVOICE_FOLDER):iffilename.lower().endswith(('.png','.jpg','.jpeg','.pdf')):filepath=os.path.join(INVOICE_FOLDER,filename)print(f"处理:{filename}")info=extract_invoice_info(filepath)results.append(info)output=json.dumps(results,ensure_ascii=False)print(f"共处理{len(results)}张发票")2.4 查重 + 校验
步骤③④:防止重复报销
importjsonfromdatetimeimportdatetime invoices=json.loads(ocr_results)# 从步骤②传入# ===== 校验规则 =====defvalidate_invoice(invoice):"""校验发票信息完整性"""errors=[]ifnotinvoice.get("invoice_no"):errors.append("未识别到发票号码")ifnotinvoice.get("amount"):errors.append("未识别到发票金额")ifnotinvoice.get("invoice_date"):errors.append("未识别到开票日期")eliffloat(invoice.get("amount","0"))<=0:errors.append("发票金额无效")returnerrors# ===== 查重 =====# 从发票台账Excel中读取已有的发票号码列表# 在影刀中用【读取Excel】获取 existing_numbersexisting_numbers=set([str(n)forninexisting_invoice_numbers])valid_invoices=[]duplicate_invoices=[]error_invoices=[]forinvininvoices:# 校验errors=validate_invoice(inv)iferrors:inv["errors"]=errors error_invoices.append(inv)continue# 查重ifinv.get("invoice_no")inexisting_numbers:duplicate_invoices.append(inv)continue# 通过inv["processed_time"]=datetime.now().strftime("%Y-%m-%d %H:%M:%S")valid_invoices.append(inv)summary={"total":len(invoices),"valid":len(valid_invoices),"duplicate":len(duplicate_invoices),"error":len(error_invoices),"valid_list":valid_invoices,"duplicate_list":duplicate_invoices,"error_list":error_invoices,}output=json.dumps(summary,ensure_ascii=False)print(f"校验完成:有效{len(valid_invoices)},重复{len(duplicate_invoices)},异常{len(error_invoices)}")2.5 写入台账
步骤⑤:Excel记录
在影刀中:
【打开Excel文件】→ D:\发票\发票台账.xlsx 【遍历列表】→ valid_list中的每条记录 【写入行】 列A:invoice_no(发票号码) 列B:amount(金额) 列C:invoice_date(开票日期) 列D:seller_name(销售方) 列E:file_path(图片路径) 列F:processed_time(处理时间) 列G:status(状态 = "待审批") 【保存并关闭】 # 将原始发票图片移动到「已处理」文件夹 【移动文件】 源文件:file_path 目标文件夹:D:\发票\已处理\2.6 提交审批
步骤⑥⑦:OA系统审批
如果你们公司有OA系统(泛微/致远/钉钉OA等):
【打开网页】→ OA系统报销页面 【填写】→ 发票号码、金额、日期等字段 【上传附件】→ 发票图片 【点击】→ 提交按钮如果用的是飞书/钉钉审批:
# 飞书审批API示例importrequestsdefsubmit_feishu_approval(invoice_info):"""提交飞书审批"""url="https://open.feishu.cn/open-apis/approval/v4/instances"headers={"Authorization":f"Bearer{FEISHU_TOKEN}","Content-Type":"application/json",}payload={"approval_code":"报销审批的approval_code","user_id":"提交人ID","form":json.dumps([{"id":"widget_invoice_no","type":"input","value":invoice_info["invoice_no"]},{"id":"widget_amount","type":"input","value":invoice_info["amount"]},{"id":"widget_date","type":"input","value":invoice_info["invoice_date"]},]),}response=requests.post(url,headers=headers,json=payload,timeout=10)returnresponse.json()三、有什么坑
坑1:OCR对发票图片质量要求高
拍照模糊、倾斜、有遮挡的发票,OCR识别率会明显下降。
提升识别率的方法:
fromPILimportImage,ImageEnhancedefpreprocess_image(image_path):"""预处理发票图片"""img=Image.open(image_path)# 1. 增强对比度(让文字更清晰)enhancer=ImageEnhance.Contrast(img)img=enhancer.enhance(1.5)# 2. 增强锐度enhancer=ImageEnhance.Sharpness(img)img=enhancer.enhance(2.0)# 3. 转为灰度(加速识别)img=img.convert('L')preprocessed_path=image_path.replace('.','_preprocessed.')img.save(preprocessed_path)returnpreprocessed_path坑2:不同发票格式,正则难写
增值税专票、普票、电子发票的版面不一样,固定的正则可能匹配不准。
解决思路:不追求100%准确,对识别失败的发一个「需人工核对」的标记,让人工介入。机器能搞定80%,剩下20%留给人。
坑3:PDF发票的OCR
PDF发票需要先转图片再OCR:
# 需要安装 pdf2image# pip install pdf2imagefrompdf2imageimportconvert_from_pathdefpdf_to_images(pdf_path):"""将PDF发票转为图片列表"""images=convert_from_path(pdf_path,dpi=200)image_paths=[]fori,imginenumerate(images):path=pdf_path.replace('.pdf',f'_page{i+1}.png')img.save(path,'PNG')image_paths.append(path)returnimage_paths坑4:发票查重依赖台账完整性
如果台账Excel被人手动删除过记录,查重就会漏掉。建议台账文件设置权限(只允许影刀写入,人工只读),或者用数据库存储。
坑5:报销金额小数点问题
OCR可能把「1280.00」识别成「1280 00」(少了小数点)。正则提取金额时要做容错:
# 金额清洗函数defclean_amount(raw_amount):"""清洗OCR识别的金额"""# 移除中文逗号、空格cleaned=raw_amount.replace(',','').replace(' ','').replace('¥','').replace('¥','')# 处理可能缺失的小数点if'.'notincleanedandlen(cleaned)>=3:# 假设最后两位是小数cleaned=cleaned[:-2]+'.'+cleaned[-2:]try:returnf"{float(cleaned):.2f}"exceptValueError:returnraw_amount总结:影刀做发票报销,OCR是关键环节。CNOCR对中文发票识别效果好,但需要预处理图片提升准确率。查重用台账Excel做简单去重,校验用正则提取关键字段。不要追求100%自动化,留一个人工复核的出口。