如何使用Python从PDF中提取表格作为文本?

ven*_*kat 32 python pdf

我有一个PDF,其中包含表格,文本和一些图像.我想在PDF中的表格中提取表格.

现在我正在手动从页面中查找表格.从那里我捕获该页面并保存到另一个PDF.

import PyPDF2

PDFfilename = "Sammamish.pdf" #filename of your PDF/directory where your PDF is stored

pfr = PyPDF2.PdfFileReader(open(PDFfilename, "rb")) #PdfFileReader object

pg4 = pfr.getPage(126) #extract pg 127

writer = PyPDF2.PdfFileWriter() #create PdfFileWriter object
#add pages
writer.addPage(pg4)

NewPDFfilename = "allTables.pdf" #filename of your PDF/directory where you want your new PDF to be
with open(NewPDFfilename, "wb") as outputStream:
    writer.write(outputStream) #write pages to new PDF
Run Code Online (Sandbox Code Playgroud)

我的目标是从整个PDF文档中提取表格.

请以PDF格式查看页面的示例图像

A S*_*ANI 45

在我看来,你有4种可能性:

  • 您可以使用tabula 直接处理pdf

  • 您可以使用pdftotext 将pdf转换为文本,然后使用python解析文本

  • 您可以使用外部工具将pdf文件转换为excel或csv,然后使用所需的python模块打开excel/csv文件.

  • 您也可以将pdf转换为图像文件,然后使用任何最新的OCR软件(从图片中自动重建表)来获取数据

您的问题与以下内容类似:

问候


Eri*_*hli 15

这个答案适用于任何遇到带有图像的 pdf 并需要使用 OCR 的人。我找不到可行的现成解决方案;没有任何东西能给我带来我所需要的准确性。

以下是我发现有效的步骤。

  1. 使用pdfimageshttps://poppler.freedesktop.org/将 pdf 页面转换为图像

  2. 使用Tesseract检测旋转并使用ImageMagick mogrify修复它。

  3. 使用 OpenCV 查找并提取表格。

  4. 使用 OpenCV 从表中查找并提取每个单元格。

  5. 使用 OpenCV 裁剪和清理每个单元格,这样就不会有干扰 OCR 软件的噪音。

  6. 使用 Tesseract 对每个单元格进行 OCR。

  7. 将每个单元格提取的文本合并为您需要的格式。

我编写了一个 python 包,其中包含可以帮助完成这些步骤的模块。

仓库: https: //github.com/eihli/image-table-ocr

文档和来源:https://eihli.github.io/image-table-ocr/pdf_table_extraction_and_ocr.html

有些步骤不需要代码,它们利用pdfimages和等外部工具tesseract。我将为需要代码的几个步骤提供一些简短的示例。

  1. 查找表:

在了解如何查找表格时,此链接是一个很好的参考。https://answers.opencv.org/question/63847/how-to-extract-tables-from-an-image/

import cv2

def find_tables(image):
    BLUR_KERNEL_SIZE = (17, 17)
    STD_DEV_X_DIRECTION = 0
    STD_DEV_Y_DIRECTION = 0
    blurred = cv2.GaussianBlur(image, BLUR_KERNEL_SIZE, STD_DEV_X_DIRECTION, STD_DEV_Y_DIRECTION)
    MAX_COLOR_VAL = 255
    BLOCK_SIZE = 15
    SUBTRACT_FROM_MEAN = -2

    img_bin = cv2.adaptiveThreshold(
        ~blurred,
        MAX_COLOR_VAL,
        cv2.ADAPTIVE_THRESH_MEAN_C,
        cv2.THRESH_BINARY,
        BLOCK_SIZE,
        SUBTRACT_FROM_MEAN,
    )
    vertical = horizontal = img_bin.copy()
    SCALE = 5
    image_width, image_height = horizontal.shape
    horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (int(image_width / SCALE), 1))
    horizontally_opened = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, horizontal_kernel)
    vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, int(image_height / SCALE)))
    vertically_opened = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, vertical_kernel)

    horizontally_dilated = cv2.dilate(horizontally_opened, cv2.getStructuringElement(cv2.MORPH_RECT, (40, 1)))
    vertically_dilated = cv2.dilate(vertically_opened, cv2.getStructuringElement(cv2.MORPH_RECT, (1, 60)))

    mask = horizontally_dilated + vertically_dilated
    contours, hierarchy = cv2.findContours(
        mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE,
    )

    MIN_TABLE_AREA = 1e5
    contours = [c for c in contours if cv2.contourArea(c) > MIN_TABLE_AREA]
    perimeter_lengths = [cv2.arcLength(c, True) for c in contours]
    epsilons = [0.1 * p for p in perimeter_lengths]
    approx_polys = [cv2.approxPolyDP(c, e, True) for c, e in zip(contours, epsilons)]
    bounding_rects = [cv2.boundingRect(a) for a in approx_polys]

    # The link where a lot of this code was borrowed from recommends an
    # additional step to check the number of "joints" inside this bounding rectangle.
    # A table should have a lot of intersections. We might have a rectangular image
    # here though which would only have 4 intersections, 1 at each corner.
    # Leaving that step as a future TODO if it is ever necessary.
    images = [image[y:y+h, x:x+w] for x, y, w, h in bounding_rects]
    return images
Run Code Online (Sandbox Code Playgroud)
  1. 从表中提取单元格。

这与 2 非常相似,因此我不会包含所有代码。我将参考的部分是对单元格进行排序。

我们想要从左到右、从上到下识别单元格。

我们将找到最左上角的矩形。然后,我们将找到中心位于该左上角矩形的顶部 y 值和底部 y 值内的所有矩形。然后我们将根据这些矩形中心的 x 值对它们进行排序。我们将从列表中删除这些矩形并重复。

def cell_in_same_row(c1, c2):
    c1_center = c1[1] + c1[3] - c1[3] / 2
    c2_bottom = c2[1] + c2[3]
    c2_top = c2[1]
    return c2_top < c1_center < c2_bottom

orig_cells = [c for c in cells]
rows = []
while cells:
    first = cells[0]
    rest = cells[1:]
    cells_in_same_row = sorted(
        [
            c for c in rest
            if cell_in_same_row(c, first)
        ],
        key=lambda c: c[0]
    )

    row_cells = sorted([first] + cells_in_same_row, key=lambda c: c[0])
    rows.append(row_cells)
    cells = [
        c for c in rest
        if not cell_in_same_row(c, first)
    ]

# Sort rows by average height of their center.
def avg_height_of_center(row):
    centers = [y + h - h / 2 for x, y, w, h in row]
    return sum(centers) / len(centers)

rows.sort(key=avg_height_of_center)
Run Code Online (Sandbox Code Playgroud)


Him*_*dar 13

我建议你用tabula提取表格.将您的pdf作为参数传递给tabula api,它将以数据框的形式将表返回给您.pdf中的每个表都作为一个数据帧返回.这是我提取pdf的代码.

#the table will be returned in a list of dataframe,for working with dataframe you need pandas
import pandas as pd
import tabula
file = "filename.pdf"
path = 'enter your directory path here'  + file
df = tabula.read_pdf(path, pages = '1', multiple_tables = True)
print(df)
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅我的回购.

  • 这仅适用于基于文本的 PDF,不适用于扫描的 PDF (3认同)
  • 对于扫描的 pdf 使用图像处理技术,有很多工具 (2认同)

小智 5

这个问题的2019年更新,因为每次我搜索“ python extract pdf table”时,我总是被引导到这里

有一个名为camelot / excalibur的python解决方案

https://github.com/atlanhq/camelot