背景说明
承接上一篇用直接从pdf提取的博文,运行后发现pdf文件处理时间过长且有些文件提取错误。
各公司文本内容形式和文件格式的可转化性各不相同,同一代码外推适用性较差。
改进思路:用acrobat的pdf格式转换是最佳选择。结合使用按键精灵脚本,依次用home,end,pageup,pagedown作为快捷键调用,每个pdf分五六步人工半自动操作对报表部分进行页面提取单独保存。运用按键精灵全自动操作将pdf通过acrobat转化为word文档。
对word文档进行代码转换的效率比较高,流程上有以下注意点:
1、对无法识别单位的表格设置跳过并报错
2、将转化成功的文件删除便于对检查剩下失败文件的具体情况,所以要做好备份
3、openpyxl,docx的运用方法延续了(连载)加强版Python提取上市公司年报报告中财务报表
4、相对于3中文章的改进之处是运用了表尾文本匹配辅助识别,参考[从招股说明书pdf文件中批量提取财务报表数据]注意上一篇文章没有考虑表内换行的情况
(https://blog.csdn.net/qq_37639139/article/details/121871651)
5、没有处理成功的表格:一类是本身报表不是很标准,另一类是格式转化时文本发生了变化,只有10份左右,可以手动补全。
6、文件名:页面提取自-代码_年份+其他后缀.docx
例如“页面提取自-688001_20190619_1.docx”
算法精炼过程
做了几个测试的脚本来检测转化出错的文件
然后观察这些文件每行第一项的特征用来完善识别条件
由于数据处理不需要非常标准的科目名称,完全可以用代码直接修改统一格式便于条件判断(这里被不知名的特殊空格恶心了一下)
仍然有两张表表头因为格式原因不能使用全等判断,没有找到原因
条件完善以后,除了转化局部乱码以及丢失表头需要直接修改源文件以外,其他意外同时出现的概率非常微乎其微,手工排查的成本降到了可以接受的水平。
代码实现
import os
import docx as d
import openpyxl as o
import re
input_path=r'C:\Users\huang\Desktop\word表 - 副本'
output_path=r'C:\Users\huang\Desktop\手工整理表单'
tle=['资产负债表','利润表','现金流量表']
os.chdir(input_path)
def tab_doc(file,order,output=output_path):
with open(file, 'rb') as f:
print('正在处理: ', file)
doc = d.Document(f)
typ = 0
f1 = 0
f2 = 0
f3=0
for dd in doc.paragraphs:
if dd.text.find('单位') != -1:
f3=1
if dd.text.find('负债表') != -1:
f1 = 1
if dd.text.find('利润表') != -1 and f3==1:
f2 = 1
if f1 == 1 and f2 == 0:
if dd.text.find('万元') != -1:
typ = 2
else:
if dd.text.find('元') != -1:
typ = 1
if typ == 0:
print(order,"、"+file+" type is wrong!")
return 0
mywb = o.Workbook()
tabs = doc.tables
flag = 1
sign = 0
j = 1
for tab in tabs:
rows = tab.rows
for row in rows:
i = 65
cells = row.cells
if flag <= 3:
head=cells[0].text
head=head.replace("\n", '')
head = head.replace(" ", '')
# 半角空格
head = head.replace(' ', '')
# tab空格
head = head.replace(' ', '')
# 试了全角空格还是没去掉
head = head.replace(' ', '')
# 没错,这是屏幕输出复制过来的神秘空格
head = head.replace("和", '及')
head = head.replace("合计", '总计')
head = head.replace("股东", '所有者')
for h in ["项目",'科目','资产']:
#这一步的判断出错会缺表,有两张表头含有未知的格式内容
if (head.find(h)!=-1) and (j == 1):
mywb.create_sheet(index=flag, title=tle[flag - 1])
mysheet = mywb[tle[flag - 1]]
for cell in cells:
c = cell.text
tmp = chr(i) + str(j)
mysheet[tmp] = c
i = i + 1
j = j + 1
break
if head not in ["项目","科目","资产","负债","负债及所有者权益"] and j > 1 :
# print("find")
if typ == 1:
for cell in cells:
c = cell.text
if i > 65:
if type(c) is type('a'):
c = c.replace("\n", '')
ResSearch = re.search(",", c)
if ResSearch != None:
c = c.replace(",", '')
cc = c.replace(".", '')
if cc.isdigit():
c = float(c)
tmp = chr(i) + str(j)
mysheet[tmp] = c
i = i + 1
j = j + 1
else:
if typ == 2:
for cell in cells:
c = cell.text
if i > 65:
if type(c) is type('a'):
ResSearch = re.search(",", c)
c = c.replace("\n", '')
if ResSearch != None:
c = c.replace(",", '')
ResSearch = re.search(".", c)
if ResSearch != None:
c = c.replace(".", '') + '00'
cc=c.replace(".", '')
if cc.isdigit():
c = float(c)
tmp = chr(i) + str(j)
mysheet[tmp] = c
i = i + 1
j = j + 1
else:
break#注意这里被转化的不是只有一张大表
if flag == 3 and (head.find('期末现金') != -1 or head.find('年末现金') != -1):
flag = 4
j = 1
else:
if flag == 2 and (head.find('净利润') != -1 or head.find('净亏损') != -1):
flag = 3
j=1
else:
if flag == 1 and head.find('权益') != -1 and head.find('总')!=-1 :
flag = 2
j = 1
mywb.save(output + "\\" + file[6:17] + ".xlsx")
print(order,"、"+file[6:17] + ".xlsx" + ' is done, type is', typ)
os.remove(file)
oo=1
for file in os.listdir():
if os.path.splitext(file)[1] == '.docx'and file[0]=="页":
tab_doc(file,oo)
oo=oo+1