import cmdimport sysimport jsonimport pdfplumberimport osfrom pprint import pprintclass FapiaoShell(cmd.Cmd):""" 发票 """intro = '欢迎使用发票提取工具,输入?(help)获取帮助消息和命令列表,CTRL+C退出程序。\n'prompt = '\n输入命令: 'doc_header = "详细文档 (输入 help <命令>):"misc_header = "友情提示:"undoc_header = "没有帮助文档:"nohelp = "*** 没有命令(%s)的帮助信息 "def __init__(self):super().__init__()def do_load(self, arg):""" 加载发票 例如:load D:\ """if not os.path.isdir(arg):print('参数必须是目录!')returnos.chdir(os.path.dirname(arg))pdfs = []for root, _, files in os.walk(arg):for fn in files:ext = os.path.splitext(fn)[1].lower()if ext != '.pdf':continuefpth = os.path.join(root, fn)fpth = os.path.relpath(fpth)print(f'发现pdf文件: {fpth}')pdfs.append(fpth)pdf_ctxs = self._parse_pdfs(pdfs)total = {'内容': pdf_ctxs,'发票数': len(pdf_ctxs),'总计': 0,}for fpth, info in pdf_ctxs:total['总计'] += float(info['总计'])print('\n保存到 结果.json...')with open("结果.json", 'w', encoding='utf-8') as json_file:json.dump(total,json_file,ensure_ascii=False,sort_keys=True,indent=4,separators=(', ', ': '))print('完成!')def _parse_pdfs(self, pdfs):""" 分析 """result = []for fpth in pdfs:info = {}with pdfplumber.open(fpth) as pdf:page = pdf.pages[0]if '增值税电子普通发票' not in ''.join(page.extract_text()):result.append((fpth, {}))inf = self._extrace_from_words(page.extract_words())info.update(inf)inf = self._extrace_from_table(page.extract_tables()[0])info.update(inf)result.append((fpth, info))return resultdef _extrace_from_words(self, words):""" 从单词中提取 """info = {}lines = {}for word in words:top = int(word['top'])bottom = int(word['bottom'])pos = (top + bottom) // 2text = word['text']if pos not in lines:lines[pos] = [text]else:lines[pos].append(text)lines_pack = []last_pos = Nonefor pos in sorted(lines):arr = lines[pos]if len(lines_pack) > 0 and pos - last_pos <= 10:lines_pack[-1] += arrcontinuelines_pack.append(arr)last_pos = poscontinuefor pack in lines_pack:for idx, line in enumerate(pack):if '电子普通发票' in line:info['标题'] = linecontinueif '发票代码:' in line:info['发票代码'] = line.split(':')[1]continueif '发票号码:' in line:info['发票号码'] = line.split(':')[1]continueif '开票日期:' in line:year = line.split(':')[1]month = [ln for ln in pack if ln.isdigit()][0]day = [ln[:2] for ln in pack if '日' in ln][0]info['开票日期'] = f'{year}-{month}-{day}'continueif '机器编号:' in line:info['机器编号'] = [ln for ln in pack if ln.isdigit()and len(ln) > 10][0]continueif '码:' in line:c1 = pack[idx].split(':')[1]c2 = pack[idx+1]c3 = pack[idx+2]c4 = pack[idx+3]info['校验码'] = f'{c1}{c2}{c3}{c4}'continueif '收款人:' in line:info['收款人'] = line.split(':')[1]continueif '开票人:' in line:info['开票人'] = line.split(':')[1]continuereturn infodef _extrace_from_table(self, table):""" 从表中提取 """info = {}if len(table) != 4:return None# 购买方for cell in table[0]:if not cell:continuelines = cell.splitlines()for line in lines:if '名 称:' in line:info['购买方名称'] = line.split(':')[1]continueif len(line) == 18 and line.isalnum():info['购买方税号'] = linecontinueif len(line) == 27:if '密码' not in info:info['密码'] = []info['密码'].append(line)continue# 详细for cell in table[1]:if not cell:continuelines = cell.splitlines()for line in lines:if '货物或应税劳务、服务名称' in line:info['商品'] = lines[1:-1]breakif '金 额' in line:info['总金额'] = lines[-1][1:]breakif '税 额' in line:info['总税额'] = lines[-1][1:]break# 合计for cell in table[2]:if not cell:continuelines = cell.splitlines()for line in lines:if '\' in line:info['总计'] = line[1:]# 销售方for cell in table[3]:if not cell:continuelines = cell.splitlines()for line in lines:if '名 称:' in line:info['销售方名称'] = line.split(':')[1]continueif len(line) == 18 and line.isalnum():info['销售方税号'] = linecontinuereturn infoif __name__ == '__main__':try:FapiaoShell().cmdloop()except KeyboardInterrupt:print('\n\n再见!')
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。