使用不正确的结构OCR从PDF中提取数据

时间:2016-07-05 15:48:57

标签: python parsing pdf ocr apache-tika

我经常流入发票pdfs。 我从这些pdf中提取数据以进行各种操作和存储。

以下是一个示例部分:enter image description here

第一步是使用Adobe的OCR。 然后,我使用tika来解析pdf。 在Python中:

from tika import parser
parsedPDF = parser.from_file("the_file.pdf")

这是预期的输出:

...
001 6 0 6 EA FSC450-WBKR FUTSAL, ADULT, WHT/BLK/RED BULK \n\n
002 6 0 6 EA SS50-P SOCCER PURPLE/BLUE/WHITE BULK \n\n
...

行由换行符分隔,您在pdf上看到的行被解析为完整行(见下文)。

这是实际输出:

001 6 0 6 \n\n
002 6 0 6 \n\n
003 13 0 13 \n\n
004 3 0 3 \n\n
EA FSC450-WBKR FUTSAL, ADULT, WHT/BLK/RED BULK \n\n
EA SS50-P SOCCER PURPLE/BLUE/WHITE BULK \n\n
...

OCR创建了一个结构,您在pdf上看到的行分为两个部分[* note]。分裂发生在"发货"和"单位"标题。 enter image description here

对于项目002,如果我从"#"前往"包装"标题,它首先在第一部分中选择数据,然后跳到第二部分的顶部。

这个问题有一个很好的解决方案吗? 有没有办法为OCR定义结构(例如,它将一行读作一行?)

[* note]:实际上文本是包装垂直(与通常看到的水平文本换行相比)。

1 个答案:

答案 0 :(得分:1)

不要试图重新制作数据,而只是使用你拥有的东西。你得到两组线,第一组包含数据行的左半部分,第二组包含右半部分。 itertools.groupby非常适合按部分分组标准拆分行。在这种情况下,您可以看出左半部分都以数字开头,而右半部分则没有。

一旦你将它们分成两个相同大小的组,使用Python的内置方法zip将它们拼接在一起。然后,一系列split()可以帮助您解析每一行的内容 - 请参阅下面代码中的注释:

from itertools import groupby

lines = """
001 6 0 6 


002 6 0 6 


003 13 0 13 


004 3 0 3 


EA FSC450-WBKR FUTSAL, ADULT, WHT/BLK/RED BULK 


EA SS50-P SOCCER PURPLE/BLUE/WHITE BULK 


EA SS30-G SOCCER BALL GREEN/WHITE #3 BULK 


EA VQ2000-RGW COMPOSITE VB ROYAL/GOLD/WHITE BULK 


""".splitlines()

# filter out empty lines
lines = filter(None, lines)

# use groupby to walk the list, and get the lines that start with 
# numbers vs those that don't - from your description, there should be
# two groups
groups = []
for _, grouplines in groupby(lines, key=lambda ll : ll[0].isdigit()):
    groups.append(list(grouplines))

# validate the input - should be two groups of line, each the same length
assert len(groups) == 2
assert len(groups[0]) == len(groups[1])

# use zip to walk the two groups together, and create list of consolidated data
consolidated = [left + right for left,right in zip(groups[0], groups[1])]

# now break these strings up into their various pieces, using a succession of split()s
parsed_lines = []
for cons_line in consolidated:
    left_items = cons_line.split(None, 4)
    right_items = left_items.pop(-1).rsplit(None,1)
    right_items, qty_type = right_items
    um, desc = right_items.split(None, 1)
    parsed_lines.append(list(map(int,left_items) + [um, desc, qty_type]))

# dump out the parsed lines
for data in parsed_lines:
    print(data)

给出:

[1, 6, 0, 6, 'EA', 'FSC450-WBKR FUTSAL, ADULT, WHT/BLK/RED', 'BULK']
[2, 6, 0, 6, 'EA', 'SS50-P SOCCER PURPLE/BLUE/WHITE', 'BULK']
[3, 13, 0, 13, 'EA', 'SS30-G SOCCER BALL GREEN/WHITE #3', 'BULK']
[4, 3, 0, 3, 'EA', 'VQ2000-RGW COMPOSITE VB ROYAL/GOLD/WHITE', 'BULK']