Java之提取PDF表格数据

  一.业务需求


       最近奇奇怪怪的需求越来越多了,最近接到一个新需求,需要定时任务拿到其他服务器的一个PDF文件,然后读取PDF里面的文件,读里面的数据记录下来;


二.技术选型

尝试过的工具包有:pdfbox、tabula。最终选用tabula

因为tabula可以读取pdf表格,解析为json或者其他格式,最主要是免费的,其实最开始的是想将PDF转excel,然后用poi读取excel数据,因为这个表格最开始就是excel模板

简单介绍一下tabula:

Tabula是一个开源工具,用于从PDF文档中提取表格数据。它的主要技术包括:

1.PDF 解析:Tabula 使用 Java 的 PDFBox 库来解析 PDF 文档的内容和布局。它可以定位到每个页的文本块和图像的坐标;
2.表格识别:Tabula 通过分析页面上的线条和文本块的布局来识别表格的结构。它会查找垂直和水平的线条作为列和行的分隔符;
3.单元格提取:在确定了表格的结构后,Tabula 会分析每个单元格对应的文本块,并提取出单元格中的文本内容;
4.数据整理:Tabula 会尝试自动整理从表格中提取的数据,例如:纵向和横向合并单元格,处理跨页的表格等。它也会执行一定的文本清理;
5.导出格式:Tabula 支持将提取出来的数据导出为 CSV 和 JSON 格式。用户可以导入到 Excel 等其他工具中进行后续分析。
6.优化算法:Tabula 在表格分析和数据提取方面使用了一些优化的算法和启发式规则,以提高正确率。同时它也提供了交互式的编辑接口供用户校正结果

 三.导入依赖

         <dependency>
            <groupId>technology.tabula</groupId>
            <artifactId>tabula</artifactId>
            <version>1.0.3</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-simple</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

四.封装的工具类

因为我只要读取表格内的数据,其他的不怎么在意,然后一切从简,下面的代码直接搬来用

@Slf4j
public class ReadPDFUtil {
    private static final SpreadsheetExtractionAlgorithm SPREADSHEEET_EXTRACTION_ALGORITHM = new SpreadsheetExtractionAlgorithm();
    private static final ThreadLocal<List<String>> THREAD_LOCAL = new ThreadLocal<>();

    public static void main(String[] args){
        // 方法一
        String filePath = "E:\\Downloads\\预防检修任务计划表.pdf";
        // 因为我是需要从第六行开始读取数据
        List<String> strings = parsePdfTableForTasks(filePath, 6);
        for (String string : strings) {
            System.out.println(string);
        }
        // 方法二,三
        //String json = readPDFcontentByFilePath(filePath);
    }

    /**
     *
     * @param pdfPath 文件路径
     * @param customStart 读取表格开始的行数
     * @return 读取的内容
     */
    public static List<String> parsePdfTableForTasks(String pdfPath, Integer customStart) {
        List<String> resultStr = new ArrayList<>(); // 存储解析后的JSON数组
        try (PDDocument document = PDDocument.load(new File(pdfPath))) {
            // 获取页面迭代器
            PageIterator pi = new ObjectExtractor(document).extract();
            // 遍历所有页面
            while (pi.hasNext()) {
                // 获取当前页
                Page page = pi.next();
                // 解析页面上的所有表格
                List<Table> tableList = SPREADSHEEET_EXTRACTION_ALGORITHM.extract(page);
                // 遍历所有表格
                for (Table table : tableList) {
                    // 获取表格中的每一行
                    List<List<RectangularTextContainer>> rowList = table.getRows();
                    // 遍历所有行并获取每个单元格信息
                    for (int rowIndex = customStart; rowIndex < rowList.size(); rowIndex++) {
                        // 获取行中的每个单元格
                        List<RectangularTextContainer> cellList = rowList.get(rowIndex);
                        StringBuilder str = new StringBuilder();
                        for (RectangularTextContainer rectangularTextContainer : cellList) {
                            String text = rectangularTextContainer.getText();
                            if (StrUtil.isEmpty(text)){
                                text=" ";
                            }
                            str.append(text).append("__");
                        }
                        resultStr.add(str.toString());
                    }
                }
            }
        } catch (IOException e) {
            log.error("读取PDF错误:{}",e.getMessage());
        } finally {
            THREAD_LOCAL.remove();
        }
        return resultStr;
    }

    /**
     * 读取PDF方法
     * @param filePath PDF的全路径
     * @return PDF内容
     */
    public static String readPDFcontentByFilePath(String filePath){
        try {
            String[] argsa = new String[]{"-f=JSON", "-p=1", filePath, "-l"};
            CommandLineParser parser = new DefaultParser();
            CommandLine cmd = parser.parse(CommandLineApp.buildOptions(), argsa);
            StringBuilder stringBuilder = new StringBuilder();
            new CommandLineApp(stringBuilder, cmd).extractTables(cmd);
            return stringBuilder.toString();
        }catch (Exception e){
            log.error("读取PDF错误:{}",e.getMessage());
        }
        return null;
    }

    /**
     * 读取PDF方法
     * @param conversionType -f导出格式,默认CSV  (一定要大写)
     * @param pageNo -p 指导出哪页,all是所有
     * @param filePath 文件路径
     * @param modeType 读取类型
     * @return -l 强制使用点阵模式提取PDF (关键在于这儿)
     */
    public static String readPDFcontentByFilePath(String conversionType,String pageNo,String filePath,String modeType){
        if(Objects.isNull(conversionType)){
            conversionType = "JSON";
        }
        if(Objects.isNull(pageNo)){
            pageNo = "all";
        }
        if(Objects.isNull(modeType)){
            modeType = "l";
        }
        try {
            String[] argsa = new String[]{"-f="+conversionType, "-p="+pageNo, filePath, "-"+modeType};
            CommandLineParser parser = new DefaultParser();
            CommandLine cmd = parser.parse(CommandLineApp.buildOptions(), argsa);
            StringBuilder stringBuilder = new StringBuilder();
            new CommandLineApp(stringBuilder, cmd).extractTables(cmd);
            return stringBuilder.toString();
        }catch (Exception e){
            log.error("读取PDF错误:{}",e.getMessage());
        }
        return null;
    }
}

我目前用的是第一个方法;运行结果是

需要根据自己需求调整一下代码就可以了,感觉还有很多bug

       以上的代码对于一般的 PDF 表格解析是基本没有问题的,但是对于带有合并单元格的解析就不能满足了。合并单元格需要考虑横向合并、纵向合并和混合合并三种合并模式,不是说 tabula-java 的 sdk 不能做只是比较麻烦,在 tabula-java 方案中我们可以获取到单元格的高和宽,那么先做一次全遍历获取二维数组对于单元格定位后,根据高和宽进行虚拟表格的建设,最后根据二维数组对数据进行回填即可。这也是用回调将单元格操作分离的原因之一,为了后面做合并单元格解析做准备的。

     等有需求我在改代码,目前就这样把,我现在只想躺平了!!!!!

### 回答1: 要提取 PDF 中的表格,可以使用 Java 库 Apache PDFBox。以下是一些示例代码,可以帮助你开始提取表格: ``` PDDocument document = PDDocument.load(new File("path/to/pdf/file.pdf")); PDFTextStripper stripper = new PDFTextStripper(); String text = stripper.getText(document); // 此时,你可以使用正则表达式或其他方法从文本中提取表格。 ``` 如果想要更高级的表格提取功能,可以考虑使用开源项目 Tabula 或 PDFTableExtractor。这些库都提供了更高级的表格提取功能,包括自动检测表格、支持多个表格等等。 ### 回答2: Java提取PDF表格通常采用Apache PDFBox库来处理。Apache PDFBox是一个开放源代码的Java库,用于操作和处理PDF文档。 首先,需要导入Apache PDFBox库的相关jar文件。然后,使用PDFTextStripper类来解析PDF文件并提取表格数据。具体步骤如下: 1. 使用PDDocument类的load()方法加载要处理的PDF文件。 2. 创建一个PDFTextStripper对象,并通过getPages()方法获取PDF的页面数量。 3. 遍历每个页面,将PDF的内容转换为字符串,可以使用PDFTextStripper类的setText()方法设置起始和结束页面。 4. 使用PDFTextStripper类的getText()方法获取页面内容的纯文本形式。 5. 根据表格的特征,通过文本处理技术来识别并提取表格数据。 6. 可以使用正则表达式或字符串处理函数来解析表格数据。 7. 将提取表格数据保存到一个数据结构中,如二维数组或集合。 8. 最后,处理完所有页面后,使用PDDocument类的close()方法关闭PDF文件。 需要注意的是,PDF文件的结构可能因具体情况而异,因此提取表格数据需要根据PDF文件的结构进行适当的调整和处理。同时,由于PDF文件可能包含复杂的布局和格式,可能需要使用其他相关库或技术来处理。 总而言之,通过使用Apache PDFBox库和适当的文本处理技术,我们可以使用Java提取PDF表格,从而获得所需的数据。 ### 回答3: Java提取PDF表格的主要方式是使用第三方库,比如Apache PDFBox和iText。这些库提供了丰富的功能,可以帮助我们读取和操作PDF文档。 首先,我们需要将PDF文件加载到Java程序中。使用PDFBox,我们可以使用PDDocument类的load方法来加载PDF文件。例如,以下代码将加载名为"example.pdf"的PDF文件: ``` PDDocument document = PDDocument.load(new File("example.pdf")); ``` 在加载PDF文件之后,我们可以使用PDFBox提供的方法来获取PDF文档中的表格内容。PDFBox并没有直接提供提取表格的方法,但我们可以通过获取文档的页面、提取文本以及对文本进行解析来达到这个目的。例如,以下代码将获取第一页中的所有文本内容: ``` PDPage page = document.getPage(0); PDFTextStripper stripper = new PDFTextStripper(); String pageText = stripper.getText(page); ``` 接下来,我们需要使用自定义的算法来解析文本内容,以提取表格的结构和数据。通常情况下,表格数据在文本中会以适当的分隔符(如制表符或空格)进行分隔,我们可以使用Java的String.split方法来分割文本行以获取每个单元格的内容。 最后,我们可以将提取到的表格数据保存到其他格式(如Excel)或进行进一步处理和分析。这可以使用Java自带的API(如Apache POI)来完成。 需要注意的是,PDF文档的结构和表格的样式可能会因文档的不同而有所差异,因此在编写提取表格的算法时需要考虑到各种情况和异常情况的处理。另外,PDFBox和iText这些库的文档和示例代码可以帮助我们更好地理解和使用它们。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值