9

我正在寻找 pdf 和 MS office 文档格式的解析器,以从文件中提取表格信息。当我看到 Apache Tika 时,我正在考虑编写单独的实现。我能够从任何这些文件格式中提取全文。但我的要求是提取表格数据,其中我期望键值格式的 2 列。我检查了网络中可用的大多数东西以寻找解决方案,但找不到任何解决方案。对此有任何指示吗?

4

4 回答 4

8

Tika 不解析表信息。实际上令人困惑的部分是它将表格标签转换为<p>实际上意味着我们丢失了结构。在当前版本 1.14 之前都是这种情况。将来这可能会得到纠正,但到目前为止还没有计划朝着这个方向努力。

你可以参考JIRA,它在 Tika 中讨论了这个缺点。在 JIRA 之后,wiki也进行了更新以反映这种不足。[免责声明:我提出了 JIRA]

现在是解决方案部分:根据我的经验,Aspose.Pdf for Java在将 pdf 转换为 html 方面做得非常出色。但它的许可。您可以通过免费试用版检查质量。代码和示例链接

于 2017-02-01T13:36:37.850 回答
6

好吧,我继续使用 apache poi 单独实现它,用于 MS 格式。我回到 Tika 获取 PDF。Tika 对文档所做的是将其输出为“基于 SAX 的 XHTML 事件” 1

所以基本上我们可以编写一个自定义的 SAX 实现来解析文件。

结构文本输出将采用以下形式(避免元细节)

<body><div class="page"><p/>
<p>Key1 Value1 </p>
<p>Key2 Value2 </p>
<p>Key3 Value3</p>
<p/>
</div>
</body>

在我们的 SAX 实现中,我们可以将第一部分视为键(对于我的问题,我已经知道键并且我正在寻找值,所以它是一个子字符串)。

用逻辑覆盖 public void characters(char[] ch, int start, int length)

请注意,对于我的情况,内容的结构是固定的,并且我知道要输入的密钥,所以这样做很容易。这不是通用解决方案

于 2012-11-26T13:53:16.097 回答
1

我使用 tika (tika-app-1.19.jar) 和 aspose (aspose-pdf-18.9.1.jar) 的组合...

我首先使用 Aspose 修改 pdf,在表格列的末尾添加管道('|')......然后将其读入 Tika 并将其转换为文本......

InputStream is = part.getInputStream(); // input-stream of PDF or PDF part

// Aspose add pipes ("|")
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Document pdfDocument   = new Document(is);   // load existing PDF file

PageCollection pageCollection = pdfDocument.getPages();
int iNumPages = pageCollection.size();

for(int i = 1; i <= iNumPages; i++)
{
    Page page = pageCollection.get_Item(i);
    TableAbsorber absorber = new TableAbsorber();// Create TableAbsorber object to find tables
    absorber.visit(page);// Visit first page with absorber

    IGenericList<AbsorbedTable> listTables = absorber.getTableList();

    for(AbsorbedTable absorbedTable : listTables)
    {
        IGenericList<AbsorbedRow> listRows = absorbedTable.getRowList();

        for(AbsorbedRow absorbedRow : listRows)
        {
            IGenericList<AbsorbedCell> listCells = absorbedRow.getCellList();

            for(AbsorbedCell absorbedCell : listCells)
            {
                TextFragmentCollection  collectionTextFrag = absorbedCell.getTextFragments();

                Rectangle rectangle = absorbedCell.getRectangle();

                // Add pipes ("|") to indicate table ends
                TextBuilder  textBuilder  = new TextBuilder(page);
                TextFragment textFragment = new TextFragment("|");
                double x = rectangle.getURX();
                double y = rectangle.getURY();
                textFragment.setPosition(new Position(x, y));
                textBuilder.appendText(textFragment);
            }
        }
    }
}
pdfDocument.save(outputStream);
is = new ByteArrayInputStream(outputStream.toByteArray()); // input-steam of modified PDF with pipes included ("|")

现在可以将表格单元格末端带有管道(“|”)的上述 pdf 输入流拉入 Tika 并更改为文本...

BodyContentHandler handler   = new BodyContentHandler();
Metadata           metadata  = new Metadata();
ParseContext       context   = new ParseContext();
PDFParser          pdfParser = new PDFParser();

PDFParserConfig config = pdfParser.getPDFParserConfig();
config.setSortByPosition(true); // needed for text in correct order
pdfParser.setPDFParserConfig(config);

//InputStream stream = new ByteArrayInputStream(sIS.getBytes(StandardCharsets.UTF_8));
pdfParser.parse(is, handler, metadata, context);
String sPdfData = handler.toString();
于 2018-10-29T05:25:36.493 回答
0

我在这里找到了一篇非常有用的博客文章,它使用ContentHandlerDecorator(使用 Groovy,但足够相似;)解析表: https ://opensource.com/article/17/8/tika-groovy

我将其调整为仅将所有部分解析<td>为制表符分隔的行,并通过以下<tr>标签收集列表中的行,因为我需要表格行保持完整,但表格单元格内没有特殊逻辑。

您可以将装饰器传递给 BodyHandler,后者将其包装为委托,如下所示:

new AutoDetectParser().parse(inputStream,
    new BodyContentHandler(new MyContentHandlerDecorator()),
    new Metadata());
于 2019-02-19T14:24:02.677 回答