最近南方的天气燥热烦闷。每天下班时头上还顶着大太阳。这么早也没啥事,就想着喊上隔壁村的二狗子去撸串。可是喊了几次都没来,一问才知道,这小子最近天天加班。
“现在生产都还没完全恢复,你们咋这么忙呢?” 这个二狗子在一家电子厂做质检员
’唉,别提了。前段时间我们供给A客户的一批支架出了点问题。现在客户要求每批货的抽检数量从10个涨到50个“
”那又不用你来检,你加个毛线班啊 “
”老哥有所不知啊,自从出了问题我们领导被客户叼了一顿后,让我每天把测量室给的几百份测量报告整理出来,把这些抽检零件的关键尺寸统计汇总出来并可视化,美其名曰看趋势,主动预防。我每天下班就忙着把几百份报告的各种数据抄录到excel,别提多痛苦了!昨天搞得太晚,抄错了几个,今天又被臭骂一顿。我都快崩溃了,这活哪是人干的呀!“
听二狗子这么一诉苦,我倒有点同情他了。不过他说的没错,抄录几百份报告这活确实不是人干的!耗费精力不说,还非常容易出错。
”你那报告长啥样子,拍个图片给我看看?我看看能不能撸一段代码帮你搞定。“
”那敢情好啊!“
说完二狗子便发了一份文档给我,我一看是个pdf的文件。一般的产品检验报告都是pdf档
部分截图如下(敏感信息已遮蔽)
图片上highlight出来的信息就是要提取的。一个是产品测量的特征,例如这里的Diameter(直径),下面是测量的结果12.2999。
每份文件代表一个零件,每个零件要测的尺寸都在报告里面,500多个文档就是500多个零件。二狗子要做的工作就是把这500多份pdf文档里面的数据整理到excel里面,这样可以用excel分析一下尺寸的变化趋势。卧槽,这要是手工来抄录,假设一个零件要统计10个尺寸,你把这10个尺寸copy到excel,在加上文件打开关闭,怎么着也要30秒。500多份妥妥的要大半天时间!
”你这个简单,我晚上回去帮你搞定!“ 我一看这个重复劳动非常适合用Python来做。现在很多办公文员都上了Python办公自动化了,怎么这个二狗子还在加班干这个。太没前途了!这个忙我得帮,不过不能白帮,搞完了得让他请吃饭。
”真的啊,太感谢你了老哥。搞好了,明天请你喝酒撸串串!“
”必须滴!“
好了,说干就干。
因为是个小项目,犯不着用Pycharm来开发,直接上Jupyter,可以顺着思路一边做一遍调试。
首先总体思路:
- 先调试一个文档,把一个pdf文档里面的内容抠出来,
- 在加上批处理逻辑,处理整个文件夹下的所有pdf档
对于一个pdf文档,思路是先pdf 转成excel,在提取出需要的信息(测量特征和数值),在把信息写入一个新建的文档。
就是这么简单!但是这里要用到一个处理pdf的第三方库,pdfplumber。这货功能很强大,可以抓取pdf文档中的文字,表格等。
import pandas as pd
import pdfplumber
path = '20200702-215_NO.1.pdf' # 要处理的pdf文件
pdf = pdfplumber.open(path)
print(pdf.pages) #查看是否能将pdf文件顺利打开
>>>[<Page:1>, <Page:2>]
测量报告确实有两页,没问题。
然后提取表格。这里出了点问题,使用extract_tables()的时候,提取到的是一个空的列表。我估计是原始的pdf文件中虽然内容是按照表格的形式来的,但是没有竖向的分割线,没有作为表格来处理。那也容易,直接提取所有的text也可以!
先把2页text合并在处理。合并后将每一行打印出来看看效果,并思考如何提取需要的信息:
pages = len(pdf.pages)
for i in range(1,pages):
all_text = pdf.pages[0].extract_text() + pdf.pages[i].extract_text()
all_text.split('\n')
效果如下,对照原始文档查看一下,信息都全了。
下面就是提取测量特征(diameter/roundness)和测试值了。这个也简单,因为在Python中字符串str有N多种操作,只要你能想到的都能搞定。
稍微思考一下,将text的所有内容按照换行符裂成个列表后,测量特征是以Diameter等开头的元素,这个元素的下一个就是值。按照这个逻辑,提取测量特征后,或者该特征元素在列表中的index, 这个index+1就是测量值的位置!so easy!
# 获取所有测量特征,作为item
item = [u for u in all_text.split('\n') if u.strip().startswith(('Diameter','Roundness'))]
# values用来记录测量值
values = []
for i in item:
j = all_text.split('\n').index(i)
value = all_text.split('\n')[j+1]
value = value.strip().split(' ')[0]
values.append(value)
print(i)
print(value)
结果如下:
所有值都有了,完美!
在创建一个DataFrame数据框,将数据存入就处理完单个文档了。这里需要思考一下DataFrame的结构。因为我们的目的是统计所有零件的所有测量特征数据值。按照一般的二维数据结构,一行代表一个零件,一列代表这些零件的一个特征,这样每处理一个文档,就在DataFrame种增加一行。因为这些零件的测量值特征都是相同,只有测量值会有差异。好了,就这么处理。
# 创建一个Dataframe来存储数值
df = pd.DataFrame(columns= item, index = None)
df.loc['first'] = values
df
效果如下:
这里,以first作为第一个零件的index写入,实际操作中可以直接以文件名来命名。至此,处理单个文件结束。下面在整个处理流程的外层嵌套一个批处理程序就大功告成了!
批处理逻辑也非常简单,把所有的测量文档都放到同一个文件夹下,调用os模块读取所有文件,这样就把所有的文件名存入一个列表了。在调用上面的逻辑遍历这个列表就把所有的测量数据写进去了就完事了。这个比较简单,这里就不罗嗦了,直接上代码和效果:
import os
files = os.listdir()
all_part = [f for f in files if f.endswith(('pdf'))]
all_part
上面是读取所有pdf文档,并将文件名放入列表all_part,结果如下:
在遍历这个列表,即可收集统计完所有数据,最后的效果如下:
效果还可以。最后在调用pandas的to+excel即可将数据写入文件,存入硬盘,任务处理完毕。我用二狗子给我的30多个文档跑了一下,我的老机器基本上5-6秒出结果。好一点的机器几百个文档1分钟以内也就搞定了!当我把这个消息告诉二狗子后,二狗子喜出望外,我在电话那头似乎都能感觉到小子鼻涕泡都乐出来了!
第二天还没下班二狗子电话就来了。
“走,老哥,羊肉串去!”
“怎么今天不加班了吗?”
“嗨,测量室4点给我报告,我撒泡尿的功夫回来一看数据就整理好了,交给老板后,老板破例让允许我提前下班。今天正好讨教讨教学学Python,这丫的威力太强大了!perfect!”
以上故事纯属虚构。
不过从这个小案例我觉得作为一个工程师,在解决实际问题时要修炼自己的两种核心能力:
第一,善于将具体的问题抽象成一个模型。只有抽象成模型后,我们才能调用我们学习的知识来解决。我们学习的线性代数,矩阵,物理方程,面向对象编码等都是高度抽象化的东西,和现实中的实际问题是有一个鸿沟的。将实际问题转化成一个模型在和学习过的知识去类比,寻找最可能的解决方案。这个套路也最终印证了知识源于实践,并指导实践的终极真理。
第二,集成创新,解决问题。如果问工业社会最伟大的创新是什么?我会毫不犹豫的回答是流水线。流水线是人类社会分工协作最极致的体现。能在最大范围调用更多人参与你的分工协作中,你就越有可能创造更大的价值。能够把一个大问题分解成若干模块,在各个击破之后组装到一起做成一个Pipeline,这就是一个解决方案的流水线。例如在上面这个案例中涉及到和操作系统交互的os模块, 处理数据以及和excel交互的pandas, 以及最核心的pdf的处理模块pdfplumber, 这些模块的具体细节我都不知道。我只要知道有这个模块,可以完成相应的功能。在借助文档学会调用它的接口即可。
这就像搭积木一样,搭建一个解决问题的流水线 :从读取pdf到text —》处理text,提取需要的信息 —》信息写入excel 在重复这个过程。就形成了一个完整的解决方案,就就是集成的概念。善于将不同的事物/知识/经验组合来解决新的问题,这种工程师能力我感觉很有前途,以后要广泛涉猎和学习,来增强这方面的能力。
注:以上文中涉及到的代码已经上传到github: https://github.com/liyongchao911/pdf2excel, 欢迎star,欢迎fork!
另外,也可以微信搜索关注公众号:‘Python数据科学家之路“ 和我交流