java&python提取PDF中的表格——PDF转Excel

前言

        在日常工作中,word、excel、pdf等是我们最常见、最常用的文件格式,那么,随之而来的问题是:如何有效地对这3种文件进行相互转换?相信大家多多少少都有遇到过这样的转换需求。其中,word与excel的相互转换以及word、excel转为pdf是比较简单,比较麻烦的是pdf转excel。由于工作需要,作者恰好遇到了pdf转excel的需求,在此分享一下个人经验,与大家相互学习。

思路

        文件格式转换的思路很简单,就是将源文件内容读取出来、然后输出保存为目标格式。下面我们用三种方式将pdf转换为excel。

java实现——spire.pdf

步骤一:引入依赖


     
         com.e-iceblue
         http://repo.e-iceblue.cn/repository/maven-public/
    




     e-iceblue
     spire.pdf
     5.1.0

ps:如果maven方式无法下载依赖,可以直接访问 Repository - Nexus Repository Manager官方仓库,将对应jar包下载下来、再通过maven安装到本地仓或直接copy到项目中添加为依赖即可。

步骤二:代码示例

public static void main(String[] args) {
     // 加载pdf文件
     PdfDocument pdf = new PdfDocument();
     pdf.loadFromFile("源.pdf");
     //保存为Excel文档
     pdf.saveToFile("目标.xlsx", FileFormat.XLSX);
     pdf.dispose();
}

优点:

  • 简单、代码量少
  • 纯代码实现,方便集成到项目
  • 所见即所得——excel里的表格与pdf里的看上去是一样的

缺点:

  • 封装的很好但是不够灵活
  • 页码、空白部分也会被输出到excel,产生多余的行列

python实现——pdfplumber+xlwt

步骤一:环境准备

  • 安装python环境
  • pip install安装pdfplumber、xlwt库
  • 项目中引入安装的库

步骤二:代码示例

from os import pread
import pdfplumber
import xlwt
# 读取源pdf文件
pdf = pdfplumber.open("./test.pdf")
# 创建excel
workboot = xlwt.Workbook()
# 创建sheet
sheet = workboot.add_sheet("Sheet1")
# 行计数
i = 0
# 循环读取pdf的每一页
for page in pdf.pages:
    # 读取表格
    for table in page.extract_tables():
        # 读取表格中的行
        for row in table:
            print(row)
            for j in range(len(row)):
                sheet.write(i,j,row[j])
            i+=1

pdf.close()
print(i)
workboot.save("./test.xlsx")

优点:

  • 代码量少,简单
  • 灵活,可以操作pdf中表格的每一个单元格
  • 纯代码实现,方便集成到项目

缺点:

  • 会忽略掉所有表格样式,相当于只粘贴文本

java配合wps软件实现——pdfbox、poi(wps vip用户请忽略以下内容^_^)

        专业的事情找专业的软件做肯定是比较完美的,wps作为免费办公软件还是想当好用的。但是,pdf转excel作为高级功能在wps里是需要开通vip会员才能使用的!不过,5页及以下pdf文件的转换对普通用户也是免费的。于是我们可以这么操作:先用代码拆分pdf、每5页保存一个文件,再使用wps将拆分后的文件分别转换;最后使用代码将转换后的excel合并为一个文件。

步骤一:引入依赖


     org.apache.pdfbox
     pdfbox
     2.0.24



    org.apache.poi
    poi
    5.0.0

步骤二:拆分pdf

/**
     * 拆分pdf
     * @param root
     * @throws Exception
     */
    public static void splitPdf(String root) throws Exception{
        File file = new File(root);
        PDDocument pdDocument = PDDocument.load(file);
        // 拆分后的文件集合保存目录
        String saveTo = root.substring(0,root.lastIndexOf("."));
        File save = new File(saveTo);
        if (!save.exists()){
            save.mkdirs();
        }
        PDPageTree pages = pdDocument.getPages();
        int count = pages.getCount();
        System.out.println(count);
        int i = 1;
        List list = new ArrayList<>();
        for (PDPage page : pages) {
            list.add(page);
            if (i % 5 == 0){
                PDDocument split = new PDDocument();
                for (int j = 0; j < 5; j++) {
                    split.addPage(list.remove(0));
                }
                // 每5页作为新的pdf输出,以数字序号命名
                split.save(new File( saveTo + "\\" + (i/5) + ".pdf"));
            }
            i++;
        }
        // 不满5页的尾数处理
        if (list.size() > 0){
            PDDocument split = new PDDocument();
            for (PDPage pdPage : list) {
                split.addPage(pdPage);
            }
            split.save(new File(saveTo + "\\" + (count/5 + 1) + ".pdf") );
        }
    }

步骤三:合并excel

/**
* 合并excel
* @param root
* @throws Exception
*/
public static void mergeExcel(String root) throws Exception{
	Workbook workbook = WorkbookFactory.create(true);
	Sheet sheet = workbook.createSheet("sheet1");
	// excel文件集合
	long count = Files.list(Paths.get(root)).filter(path -> path.toString().endsWith(".xlsx")).count();
	System.out.println("总数:" + count);

	int rowNumber = 0;
	// excel文件以数字序列命名的
	for (long l = 1; l <= count; l++) {
		String fileName = root + l + ".xlsx";
		System.out.println("处理:" + fileName);
		Workbook temp = WorkbookFactory.create(new File(fileName));
		Sheet tempSheet = temp.getSheetAt(0);
		for (Row cells : tempSheet) {
			Row newRow = sheet.createRow(rowNumber++);
			// 复制行格式
			newRow.setHeight(cells.getHeight());
			newRow.setHeightInPoints(cells.getHeightInPoints());
			newRow.setRowStyle(cells.getRowStyle());
			newRow.setZeroHeight(cells.getZeroHeight());
			int cellNumber = 0;
			for (Cell cell : cells) {
				// 复制列格式
				sheet.setColumnWidth(cellNumber,tempSheet.getColumnWidth(cellNumber));
				// 复制数据
				Cell newCell = newRow.createCell(cellNumber);
				CellType cellType = CellType.valueOf(cell.getCellType().name());
				switch (cellType){
					case STRING:
						newCell.setCellValue(cell.getStringCellValue());
						break;
					case BLANK:
						newCell.setBlank();
						break;
					case NUMERIC:
						newCell.setCellValue(cell.getNumericCellValue());
						break;
					default:

				}
				// 复制单元格格式
				newCell.setCellComment(cell.getCellComment());
				CellStyle cellStyle = workbook.createCellStyle();
				cellStyle.cloneStyleFrom(cell.getCellStyle());
				newCell.setCellStyle(cellStyle);
				newCell.setHyperlink(cell.getHyperlink());
				cellNumber++;
			}
		}
		// 合并单元格设置,合并区域的坐标需要根据上个文件的行数调整
		int lastRowNum = sheet.getLastRowNum() - tempSheet.getLastRowNum();
		for (CellRangeAddress mergedRegion : tempSheet.getMergedRegions()) {
			int firstRow = mergedRegion.getFirstRow();
			int lastRow = mergedRegion.getLastRow();
			mergedRegion.setFirstRow(firstRow + lastRowNum);
			mergedRegion.setLastRow(lastRow + lastRowNum);
			sheet.addMergedRegion(mergedRegion);
		}
		temp.close();
	}
	workbook.write(new FileOutputStream(root + "合并.xlsx"));
	workbook.close();
}

优点:

  • 免费使用专业软件的付费功能
  • pdf里的表格样式可以完美复制

缺点:

  • 非纯代码实现,不能集成到项目
  • 实现比较复杂,代码量多

总结

        以上方式仅适用于pdf里的表格为非图片形式,如果表格以图片形式存在于pdf里是无法转换的。可能需要使用图文识别方面的技术,有了解的小伙伴可以留言哈。(wps是支持图片形式的表格识别的,不过作者没有亲测,vip可用)

        

你可能感兴趣的:(随记,python,java,经验分享)