Python实现增值税发票批量识别和导出到表格(已附程序下载链接)
·
看到办公室财务同事审核发票报销单,一张一张的手动填进表格里,每次一弄就是一天。 领导问我有没有什么办法。查了一些资料,用百度云OCR文字识别接口+Python做了一套程序。
实现了拍摄的发票照片和保存的发票PDF批量识别,
目前稳定运行了两个月,
效率从原来耗时1-2天,提升到5分钟左右
程序打包后的exe文件,需要可以自取
程序下载链接:https://pan.quark.cn/s/8de38589e9be
一、背景:财务同事的"发票地狱"
在我们公司,财务部每月都要处理大量发票报销单。每次报销季,财务同事都要:
-
手动录入:一张一张查看发票信息
-
Excel填表:把发票号、金额、日期等数据敲进表格
-
人工核对:检查金额、税率是否匹配
-
重复劳动:每月重复相同流程,耗时1-2天
痛点:
-
效率低:几百张发票手动录入,费时费力
-
易出错:人工输入难免手误,导致后续对账麻烦
-
重复劳动:每月都在做同样的事情,毫无技术含量
二、解决方案:Python + OCR自动化识别
1. 技术选型
✅ 百度云OCR(高精度发票识别API)
✅ Python(自动化处理+Excel操作)
2. 系统架构
📂 发票自动化系统 ├── 📜 main.py # 主程序(OCR调用+数据处理) ├── 📜 config.txt # API密钥等配置
3. 核心功能
-
OCR识别:自动提取发票信息
-
数据校验:自动检查金额、税率是否合规
-
Excel导出:生成结构化数据,可直接导入财务系统
-
日志记录:记录处理结果,方便后续排查
三、效果对比
| 指标 | 传统方式 | 自动化系统 | 提升效果 |
|---|---|---|---|
| 处理时间 | 1-2天 | 5分钟 | 95%+ |
| 准确率 | 人工易出错 | 99%+ | 大幅提高 |
| 人力成本 | 需专人处理 | 无人值守 | 解放人力 |
| 可扩展性 | 难以批量处理 | 支持1000+张 | 弹性扩容 |
实际运行效果:
-
2个月0出错,财务同事再也不用加班录发票
-
5分钟处理500+张发票,效率提升近百倍
-
领导点赞,准备推广到全公司使用
四、关键技术实现
1. OCR发票识别(百度云API)
def process_image(self, image_path):
max_retries = 10
retry_delay = 0.2
for attempt in range(max_retries):
try:
with open(image_path, 'rb') as f:
image_data = base64.b64encode(f.read()).decode()
if not self.access_token:
self.get_access_token()
current_time = time.time()
elapsed_time = current_time - self.last_request_time
if elapsed_time < 0.2:
time.sleep(0.2 - elapsed_time)
self.last_request_time = time.time()
response = requests.post(
url='https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice',
headers={'Content-Type': 'application/x-www-form-urlencoded'},
params={'access_token': self.access_token},
data={'image': image_data},
timeout=15
)
response.raise_for_status()
result = response.json()
if 'error_code' in result:
if result['error_code'] == 18:
time.sleep(retry_delay)
continue
else:
logger.error(f"API错误 {result['error_code']}: {result['error_msg']}")
return []
return self.parse_ocr_result(result)
except Exception as e:
logger.error(f"处理图片失败 {image_path}: {str(e)}")
time.sleep(retry_delay)
continue
logger.error(f"处理图片失败 {image_path}: 达到最大重试次数")
return []
2. 数据清洗 & Excel导出
def save_to_excel(self, data, output_path, export_detail):
try:
book = xlwt.Workbook(encoding='utf-8')
sheet = book.add_sheet('发票数据')
style = xlwt.XFStyle()
font = xlwt.Font()
font.name = '微软雅黑'
font.height = 220
style.font = font
headers = ['源文件名', '发票类型', '发票号码', '开票日期', '购买方名称', '购买方纳税人识别号', '销售方名称',
'销售方纳税人识别号']
if export_detail:
headers += ['项目名称', '规格型号', '单位', '数量', '单价', '金额', '税率/征收率', '税额']
headers += ['金额合计', '税额合计', '价税合计', '备注', '开票人']
for col, header in enumerate(headers):
sheet.write(0, col, header, style)
sheet.col(col).width = 8000
for row_idx, record in enumerate(data, 1):
col = 0
sheet.write(row_idx, col, record.get('source_file', ''), style)
col += 1
sheet.write(row_idx, col, record.get('invoicetype', ''), style)
col += 1
sheet.write(row_idx, col, record.get('invoice_no', ''), style)
col += 1
sheet.write(row_idx, col, record.get('invoice_date', ''), style)
col += 1
sheet.write(row_idx, col, record.get('purchaser', ''), style)
col += 1
sheet.write(row_idx, col, record.get('purchaserRegisterNum', ''), style)
col += 1
sheet.write(row_idx, col, record.get('seller', ''), style)
col += 1
sheet.write(row_idx, col, record.get('sellerRegisterNum', ''), style)
col += 1
if export_detail:
sheet.write(row_idx, col, record.get('name', ''), style)
col += 1
sheet.write(row_idx, col, record.get('type', ''), style)
col += 1
sheet.write(row_idx, col, record.get('unit', ''), style)
col += 1
sheet.write(row_idx, col, record.get('num', ''), style)
col += 1
sheet.write(row_idx, col, record.get('price', ''), style)
col += 1
sheet.write(row_idx, col, record.get('amount', ''), style)
col += 1
sheet.write(row_idx, col, record.get('rate', ''), style)
col += 1
sheet.write(row_idx, col, record.get('tax', ''), style)
col += 1
sheet.write(row_idx, col, record.get('total_amount', ''), style)
col += 1
sheet.write(row_idx, col, record.get('total_tax', ''), style)
col += 1
sheet.write(row_idx, col, record.get('amount_infiguers', ''), style)
col += 1
sheet.write(row_idx, col, record.get('remarks', ''), style)
col += 1
sheet.write(row_idx, col, record.get('noteDrawer', ''), style)
book.save(output_path)
logger.info(f"Excel文件保存成功: {output_path}")
except Exception as e:
logger.error(f"保存Excel失败: {str(e)}")
raise
3. 日志记录
def setup_logger():
try:
log_dir = os.getcwd()
logger = logging.getLogger("PDFTool")
logger.setLevel(logging.INFO)
formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
file_handler = logging.handlers.TimedRotatingFileHandler(
filename=os.path.join(log_dir, 'pdf_tool.log'),
when='midnight',
backupCount=7,
encoding='utf-8'
)
file_handler.setFormatter(formatter)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
except PermissionError:
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("PDFTool")
logger.warning("无法创建日志目录,将日志输出到控制台")
return logger


更多推荐


所有评论(0)