影刀RPA OCR票据识别实战:发票合同证件自动提取 影刀RPA OCR票据识别实战发票合同证件自动提取作者林焱什么情况用财务和行政每天被一堆票据淹没增值税发票、出租车票、合同扫描件、营业执照、身份证。每一张都需要把关键信息金额、日期、公司名、证件号手动录入到Excel或ERP系统里。你可能会想「OCR不是早就有了吗」问题在于——通用OCR是把所有文字一股脑倒出来你拿到的是「京市海区中关村街华大厦B1902」这种断句错乱的杂烩。真正的票据识别需要的是结构化提取从发票里准确定位「金额¥3,580.00」而不是把整张纸上的字全糊成一团。核心场景需要从标准格式票据中精准提取结构化字段的自动化任务。怎么做第一步搞清票据类型每种处理方式不同拼多多店群自动化上架方案票据类型关键字段推荐方案增值税发票发票号、金额、税额、开票日期、公司名百度发票识别API / 阿里发票OCR身份证姓名、身份证号、地址、有效期百度身份证识别营业执照统一社会信用代码、公司名、法人百度营业执照识别出租车票金额、上下车时间、车牌号通用OCR 正则解析合同扫描件甲方、乙方、金额、签署日期PaddleOCR NER提取划重点对于发票和身份证这类标准票据直接用百度/阿里的专用票据识别接口准确率95%以上。别自己拿通用OCR硬怼。第二步百度智能云票据识别接入百度每天给500次免费调用小规模使用完全够。importrequestsimportbase64importjsonclassBaiduInvoiceOCR:百度智能云增值税发票识别def__init__(self,api_key,secret_key):self.api_keyapi_key self.secret_keysecret_key self.access_tokenNonedef_get_token(self):获取access_token有效期30天缓存一下urlhttps://aip.baidubce.com/oauth/2.0/tokenparams{grant_type:client_credentials,client_id:self.api_key,client_secret:self.secret_key}resprequests.post(url,paramsparams,timeout30)self.access_tokenresp.json()[access_token]returnself.access_tokendefrecognize_vat_invoice(self,image_path):识别增值税发票ifnotself.access_token:self._get_token()# 读取图片转base64withopen(image_path,rb)asf:img_base64base64.b64encode(f.read()).decode()urlfhttps://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoiceparams{access_token:self.access_token}headers{Content-Type:application/x-www-form-urlencoded}# 图片base64需要URL编码fromurllib.parseimportquote datafimage{quote(img_base64)}resprequests.post(url,paramsparams,headersheaders,datadata,timeout30)resultresp.json()ifwords_resultnotinresult:return{error:result.get(error_msg,未知错误)}wordsresult[words_result]# 提取关键字段同时做数据清洗return{invoice_code:words.get(InvoiceCode,),invoice_num:words.get(InvoiceNum,),invoice_date:words.get(InvoiceDate,),seller_name:words.get(SellerName,),buyer_name:words.get(PurchaserName,),amount:words.get(AmountInWords,),# 大写金额amount_figures:words.get(AmountInFiguers,),# 小写金额total_amount:words.get(TotalAmount,),total_tax:words.get(TotalTax,),check_code:words.get(CheckCode,)}# 使用# ocr BaiduInvoiceOCR(your_api_key, your_secret_key)# result ocr.recognize_vat_invoice(invoice_001.jpg)# print(json.dumps(result, ensure_asciiFalse, indent2))第三步身份证识别身份证识别比发票更简单百度的身份证接口专门针对卡证优化过。classBaiduIDCardOCR:百度身份证识别def__init__(self,api_key,secret_key):self.api_keyapi_key self.secret_keysecret_key self.access_tokenNonedef_get_token(self):urlhttps://aip.baidubce.com/oauth/2.0/tokenparams{grant_type:client_credentials,client_id:self.api_key,client_secret:self.secret_key}resprequests.post(url,paramsparams)self.access_tokenresp.json()[access_token]defrecognize(self,image_path,sidefront): side: front正面有照片那面, back反面有签发机关那面 ifnotself.access_token:self._get_token()withopen(image_path,rb)asf:img_base64base64.b64encode(f.read()).decode()urlhttps://aip.baidubce.com/rest/2.0/ocr/v1/idcardparams{access_token:self.access_token}fromurllib.parseimportquote datafid_card_side{side}image{quote(img_base64)}resprequests.post(url,paramsparams,headers{Content-Type:application/x-www-form-urlencoded},datadata,timeout30)resultresp.json()wordsresult.get(words_result,{})ifsidefront:return{name:words.get(姓名,{}).get(words,),id_number:words.get(公民身份号码,{}).get(words,),gender:words.get(性别,{}).get(words,),ethnicity:words.get(民族,{}).get(words,),birth_date:words.get(出生,{}).get(words,),address:words.get(住址,{}).get(words,)}else:return{issuing_authority:words.get(签发机关,{}).get(words,),valid_from:words.get(签发日期,{}).get(words,),valid_to:words.get(失效日期,{}).get(words,)}第四步出租车票——通用OCR正则提取出租车票没有专用API但格式相对固定。用通用OCR拿到所有文字然后用正则表达式把金额、时间抠出来。importreclassTaxiReceiptParser:出租车票解析——通用OCR 正则提取def__init__(self):frompaddleocrimportPaddleOCR self.ocrPaddleOCR(use_angle_clsTrue,langch)defparse(self,image_path):解析出租车票resultself.ocr.ocr(image_path,clsTrue)# 把所有识别的文字拼在一起all_textifresultandresult[0]:forlineinresult[0]:all_textline[1][0]\n# 正则提取关键信息parsed{amount:self._extract_amount(all_text),date:self._extract_date(all_text),time:self._extract_time(all_text),plate_number:self._extract_plate(all_text),taxi_company:self._extract_company(all_text)}returnparseddef_extract_amount(self,text):提取金额——找「金额」或「合计」后面的数字patterns[r(?:金额|合计|总计)[:\s]*[¥]?\s*(\d\.?\d*),r[¥]\s*(\d\.?\d*),r(\d\.\d{2})\s*元]forpinpatterns:matchre.search(p,text)ifmatch:returnfloat(match.group(1))returnNonedef_extract_date(self,text):提取日期matchre.search(r(\d{4})[年/-](\d{1,2})[月/-](\d{1,2}),text)ifmatch:returnf{match.group(1)}-{match.group(2).zfill(2)}-{match.group(3).zfill(2)}returnNonedef_extract_time(self,text):提取上下车时间matchre.search(r(\d{1,2}):(\d{2}),text)ifmatch:returnf{match.group(1).zfill(2)}:{match.group(2)}returnNonedef_extract_plate(self,text):提取车牌号matchre.search(r([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤川青藏琼宁][A-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]),text)ifmatch:returnmatch.group(1)returnNonedef_extract_company(self,text):提取出租车公司名matchre.search(r(.*?)(?:出租|的士),text)ifmatch:returnmatch.group(1).strip()returnNone第五步批量处理流水线把上面这些串起来做一个完整的票据处理流水线。importosimportpandasaspdfromdatetimeimportdatetimeclassReceiptPipeline:票据批量处理流水线def__init__(self,baidu_api_key,baidu_secret_key):self.invoice_ocrBaiduInvoiceOCR(baidu_api_key,baidu_secret_key)self.idcard_ocrBaiduIDCardOCR(baidu_api_key,baidu_secret_key)self.taxi_parserTaxiReceiptParser()defdetect_type_by_filename(self,filename):根据文件名推断票据类型namefilename.lower()if发票innameorinvoiceinname:returninvoiceelif身份证innameoridcardinnameorid_cardinname:returnidcardelif出租车innameortaxiinnameor的士inname:returntaxielse:returnunknowndefprocess_folder(self,folder_path,output_excel):批量处理文件夹中的所有票据results[]image_extensions(.png,.jpg,.jpeg,.bmp,.tiff)files[fforfinos.listdir(folder_path)iff.lower().endswith(image_extensions)]forfilenameinfiles:pathos.path.join(folder_path,filename)doc_typeself.detect_type_by_filename(filename)try:ifdoc_typeinvoice:dataself.invoice_ocr.recognize_vat_invoice(path)elifdoc_typeidcard:dataself.idcard_ocr.recognize(path,front)elifdoc_typetaxi:dataself.taxi_parser.parse(path)else:data{error:无法识别的票据类型}data[source_file]filename data[processed_at]datetime.now().isoformat()results.append(data)exceptExceptionase:results.append({source_file:filename,error:str(e)})# 导出到Exceldfpd.DataFrame(results)df.to_excel(output_excel,indexFalse)print(f处理完成{len(results)}张票据 →{output_excel})returndf有什么坑坑1发票扫描件质量参差不齐纸质发票拍照的时候经常出现光线不均匀一半亮一半暗、角度倾斜、褶皱、手指遮挡。百度的专用接口对这种有一定容错但严重模糊的图片正确率会断崖下降。踩坑实录有次处理财务交来的300张发票用手机拍的那种角度歪了45度金额识别率不到60%。后来加了一道预处理用OpenCV做透视变换校正角度准确率回到90%以上。TEMU店群如何管理运营坑2百度API token只有30天有效期很多人写好代码跑了一个月后突然报错「access_token invalid or no longer valid」。百度access_token有效期30天需要自动续期。解决方法每次调用前检查token是否过期或者在代码里做好异常处理token失效时自动重新获取。坑3金额数字识别的「大小写不一致」发票上同时有大写金额叁仟伍佰捌拾元整和小写金额¥3,580.00但OCR偶尔会把其中之一识别错。如果你只依赖其中一个做校验可能会有偏差。解决方法同时提取大小写金额做交叉校验。不一致时标记为「人工复核」。坑4PaddleOCR在CPU上处理大图很慢一张300DPI的A4扫描件约2000x3000像素PaddleOCR在CPU上可能要跑10秒以上。批量处理几百张的时候熬死人。解决方法在OCR前先缩小图片保持宽高比缩到1000像素宽对文字识别几乎没有影响但速度能快5-10倍。坑5影刀的截图和文件路径问题在影刀里做截图传给Python节点要注意路径问题。影刀截图默认存在临时目录路径可能包含中文或空格PaddleOCR可能读不到。解决方法在Python节点里用绝对路径或者先把文件复制到纯英文路径下再处理。总结票据OCR的核心不是「能不能识别」而是「能不能结构化提取」。发票身份证用专用接口一步到位通用票据用通用OCR正则提取兜底。关键是做好预处理和后校验别让OCR的漏网之鱼进了财务系统。