客至汲泉烹茶, 抚琴听者知音

一个批量提取pdf中数据并转化为excel的方案介绍

前言

我需要国家重点监控企业名单数据,网上找了一下,最新数据是2017年,网址如下:https://www.cenews.com.cn/company/201711/t20171116_858015.html。写个爬虫脚本把所有pdf下载下来后,遇到个大问题:pdf肯定是无法直接使用的,我需要把他们转为DataFrame格式方便分析。

python方案

首先我用的肯定是python,搜了一下貌似大家对Tabula的评价挺好的,于是下载下来试一下。

[scode type="yellow"]注意:Tabula需要java环境,如果你懒得装,可以试一下另外两个库:pdfminer,pdf2htmlEX,我是没有测试[/scode]

首先安装

pip install tabula-py

使用起来也很简单,一行代码的事

import tabula
df = tabula.read_pdf(r"北京.pdf", encoding='gbk', pages='all')
print(df)
# 如果pdf有多页,那么返回的是一个列表,需要用循环语句提取df

那么效果如何呢?

先看下初始文档:

再看下提取结果:

emmm……可以看到最明显的问题是pdf一旦有跨行的数据,那么tabula就无法准确识别。后面还有缺行的情况,怎么看都无法处理。

PDFelement方案

换个思路,专业的事交给专业的人办。PDFelement是一款pdf处理工具,可以用来转为excel,当然,也有批量处理功能。

那么效果如何呢?

可以看到已经基本可用了,如果你只是处理单文档,那么到此就可以结束了。(PS:pdf有六页,所以分了六个sheet)

除此之外,PDFelement还有ocr识别扫描文档功能,不过要下载一个大约300多M的包,试了一下效果也挺好的。

但是,因为我们pdf数量比较多,每个pdf又有很多页,手工处理实在太麻烦,因此我们用python再处理一下。

进一步批量处理

这里就要请出我们的老朋友pandas了。

首先读取转化后的excel看看是什么样子的。

可以看出主要需要处理的是以下部分:

  • 前几行的无效数据
  • 跨行所造成的无效列(比如最后一列全是空值)
  • 元素中的换行符\n

除此之外,我们还需要循环excel表中的所有sheet,还想添加一列省份和名单类别。

废话不多说,直接上代码:

def excel_process():
    file_path = r'E:\data\安徽.xlsx'
    province = file_path.split('\\')[-1][:-5]
    xl = pd.ExcelFile(file_path)
    sheet_list = xl.sheet_names # 检索所有的sheet名
    tran = {1:'废水国家重点监控企业名单',2:'废气国家重点监控企业名单',
            3:'污水处理厂国家重点监控企业名单',4:'重金属企业国家重点监控企业名单',
            5:'危险废物国家重点监控企业名单'}
    count = 0
    for sheet in sheet_list:
        df = pd.read_excel(file_path,sheet_name = sheet)
        col_list = df.columns.values.tolist()    
        skiprows= 1
        try:
            while ('Unnamed' in col_list[1]) and ('Unnamed' in col_list[2]) and ('Unnamed' in col_list[3]): 
            # 前几行无效的主要标识是有Unnamed字段,写个循环按行依此判断
                if skiprows >10: # 有些sheet(主要是首页)可能是空的,跳出循环
                    break
                df = pd.read_excel(file_path,skiprows= skiprows,sheet_name = sheet)
                # 读取excel的sheet_name表,跳过前skiprows行
                col_list = df.columns.values.tolist()
                skiprows +=1
        except:
            continue

        if 1<skiprows <10: # 如果表格第一行没有数据,那么说明名单换为下一个了
            count +=1

        drop_list = [x for x in col_list if 'Unnamed' in x] # 删除无效列
        df = df.drop(drop_list,axis=1)
        alist = ['市', '县', '组织机构代码', '企业名称'] # 重命名列名
        df.columns = alist
        for item in alist:
            if item == '组织机构代码': 
            # 不知道为什么处理组织机构代码这一列时会造成大量数据缺失,因此跳过
                continue
            df[item] = pd.Series(df[item]).str.replace('[\n\s]+', '')
            # 删掉换行符
        
        df['省'] = province
        df['类别'] = tran[count]
        df=df.dropna(subset=['企业名称']) 
        # 有些表最下面有页数,会造成多余的行,删除这些行即可
        df.insert(0, '省', df.pop('省'))# 调整顺序,把省这一列放到第一列    
        print(df)

这是适用于此次数据处理的方案,你有需求的话可以参考代码自行修改。

最终处理效果如下图:

添加新评论