使用 Document AI (Python) 进行表单解析

1. 简介

在此 Codelab 中,您将学习如何使用 Document AI Form Parser 通过 Python 解析手写表单。

我们将以简单的医疗登记表为例,但此流程适用于 DocAI 支持的任何通用表单。

前提条件

此 Codelab 以其他 Document AI Codelab 中展示的内容为基础。

建议您先完成以下 Codelab,然后再继续。

学习内容

  • 如何使用 Document AI Form Parser 解析和提取扫描表单中的数据。

所需条件

  • Google Cloud 项目
  • 一个浏览器,例如 ChromeFirefox
  • 了解 Python 3

调查问卷

您将如何使用本教程?

仅阅读教程内容 阅读并完成练习

您如何评价使用 Python 的体验?

新手水平 中等水平 熟练水平

您如何评价自己在使用 Google Cloud 服务方面的经验水平?

<ph type="x-smartling-placeholder"></ph> 新手 中级 熟练

2. 设置和要求

此 Codelab 假定您已完成 Document AI OCR Codelab 中列出的 Document AI 设置步骤。

请先完成以下步骤,然后再继续:

您还需要安装 Pandas,这是一个适用于 Python 的开源数据分析库。

pip3 install --upgrade pandas

3. 创建表单解析器处理器

您必须先创建一个 Form Parser 处理器实例,以便在本教程的 Document AI Platform 中使用。

  1. 在控制台中,导航到 Document AI Platform 概览
  2. 点击创建处理器,然后选择表单解析器处理器
  3. 指定处理器名称并从列表中选择您的区域。
  4. 点击 Create 以创建处理器
  5. 复制处理器 ID。您稍后必须在代码中使用此 ID。

Cloud 控制台中的测试处理器

您可以通过上传文档在控制台中测试处理器。点击 Upload Document(上传文件),然后选择要解析的表单。如果您没有可用的示例表单,可以下载并使用该表单。

运行状况表单

输出应如下所示:解析后的表单

4. 下载示例表单

我们提供了一个包含简单医疗登记表的示例文件。

您可以通过以下链接下载 PDF 文件。然后将其上传到 Cloud Shell 实例

或者,您也可以使用 gsutil 从我们的公开 Google Cloud Storage 存储分区下载该文件。

gsutil cp gs://cloud-samples-data/documentai/codelabs/form-parser/intake-form.pdf .

使用以下命令确认文件已下载到您的 Cloud Shell:

ls -ltr intake-form.pdf

5. 提取表单键值对

在此步骤中,您将使用在线处理 API 调用之前创建的表单解析器处理器。然后,您将提取在文档中找到的键值对。

在线处理是指发送单个文档并等待响应。如果要发送多个文件,或者文件大小超过在线处理网页数上限,您也可以使用批处理。您可以在 OCR Codelab 中查看如何执行此操作。

除了处理器 ID 之外,用于发出进程请求的代码对于每种处理器类型都是相同的。

Document 响应对象包含输入文档中的页面列表。

每个 page 对象都包含一系列表单字段及其在文本中的位置。

以下代码会遍历每个页面,并提取每个键、值和置信度分数。这类结构化数据有助于更轻松地存储在数据库中或在其他应用中使用。

创建一个名为 form_parser.py 的文件,并使用以下代码。

form_parser.py

import pandas as pd
from google.cloud import documentai_v1 as documentai


def online_process(
    project_id: str,
    location: str,
    processor_id: str,
    file_path: str,
    mime_type: str,
) -> documentai.Document:
    """
    Processes a document using the Document AI Online Processing API.
    """

    opts = {"api_endpoint": f"{location}-documentai.googleapis.com"}

    # Instantiates a client
    documentai_client = documentai.DocumentProcessorServiceClient(client_options=opts)

    # The full resource name of the processor, e.g.:
    # projects/project-id/locations/location/processor/processor-id
    # You must create new processors in the Cloud Console first
    resource_name = documentai_client.processor_path(project_id, location, processor_id)

    # Read the file into memory
    with open(file_path, "rb") as image:
        image_content = image.read()

        # Load Binary Data into Document AI RawDocument Object
        raw_document = documentai.RawDocument(
            content=image_content, mime_type=mime_type
        )

        # Configure the process request
        request = documentai.ProcessRequest(
            name=resource_name, raw_document=raw_document
        )

        # Use the Document AI client to process the sample form
        result = documentai_client.process_document(request=request)

        return result.document


def trim_text(text: str):
    """
    Remove extra space characters from text (blank, newline, tab, etc.)
    """
    return text.strip().replace("\n", " ")


PROJECT_ID = "YOUR_PROJECT_ID"
LOCATION = "YOUR_PROJECT_LOCATION"  # Format is 'us' or 'eu'
PROCESSOR_ID = "FORM_PARSER_ID"  # Create processor in Cloud Console

# The local file in your current working directory
FILE_PATH = "intake-form.pdf"
# Refer to https://cloud.google.com/document-ai/docs/processors-list
# for supported file types
MIME_TYPE = "application/pdf"

document = online_process(
    project_id=PROJECT_ID,
    location=LOCATION,
    processor_id=PROCESSOR_ID,
    file_path=FILE_PATH,
    mime_type=MIME_TYPE,
)

names = []
name_confidence = []
values = []
value_confidence = []

for page in document.pages:
    for field in page.form_fields:
        # Get the extracted field names
        names.append(trim_text(field.field_name.text_anchor.content))
        # Confidence - How "sure" the Model is that the text is correct
        name_confidence.append(field.field_name.confidence)

        values.append(trim_text(field.field_value.text_anchor.content))
        value_confidence.append(field.field_value.confidence)

# Create a Pandas Dataframe to print the values in tabular format.
df = pd.DataFrame(
    {
        "Field Name": names,
        "Field Name Confidence": name_confidence,
        "Field Value": values,
        "Field Value Confidence": value_confidence,
    }
)

print(df)

现在运行代码,您应该会看到提取并输出的文本。

如果使用我们的示例文档,您应该会看到以下输出内容:

$ python3 form_parser.py
                                           Field Name  Field Name Confidence                                        Field Value  Field Value Confidence
0                                            Phone #:               0.999982                                     (906) 917-3486                0.999982
1                                  Emergency Contact:               0.999972                                         Eva Walker                0.999972
2                                     Marital Status:               0.999951                                             Single                0.999951
3                                             Gender:               0.999933                                                  F                0.999933
4                                         Occupation:               0.999914                                  Software Engineer                0.999914
5                                        Referred By:               0.999862                                               None                0.999862
6                                               Date:               0.999858                                            9/14/19                0.999858
7                                                DOB:               0.999716                                         09/04/1986                0.999716
8                                            Address:               0.999147                                     24 Barney Lane                0.999147
9                                               City:               0.997718                                             Towaco                0.997718
10                                              Name:               0.997345                                       Sally Walker                0.997345
11                                             State:               0.996944                                                 NJ                0.996944
...

6. 解析表

Form Parser 还可以从文档内的表格中提取数据。在此步骤中,我们将下载新的示例文档并从表中提取数据。由于我们将数据加载到 Pandas 中,因此只需调用一次方法,即可将这些数据输出为 CSV 文件和许多其他格式。

下载包含表格的示例表单

我们提供了一个示例文档,其中包含一个示例表单和一个表格。

您可以通过以下链接下载 PDF 文件。然后将其上传到 Cloud Shell 实例

或者,您也可以使用 gsutil 从我们的公开 Google Cloud Storage 存储分区下载该文件。

gsutil cp gs://cloud-samples-data/documentai/codelabs/form-parser/form_with_tables.pdf .

使用以下命令确认文件已下载到您的 Cloud Shell:

ls -ltr form_with_tables.pdf

提取表数据

表数据的处理请求与提取键值对的请求完全相同。不同之处在于我们从响应中提取数据的字段。表数据存储在 pages[].tables[] 字段中。

此示例从每个表格和页面的表格标题行和正文行中提取“ ”的相关信息,然后输出表格并将其保存为 CSV 文件。

创建一个名为 table_parsing.py 的文件,并使用以下代码。

table_parsing.py

# type: ignore[1]
"""
Uses Document AI online processing to call a form parser processor
Extracts the tables and data in the document.
"""
from os.path import splitext
from typing import List, Sequence

import pandas as pd
from google.cloud import documentai


def online_process(
    project_id: str,
    location: str,
    processor_id: str,
    file_path: str,
    mime_type: str,
) -> documentai.Document:
    """
    Processes a document using the Document AI Online Processing API.
    """

    opts = {"api_endpoint": f"{location}-documentai.googleapis.com"}

    # Instantiates a client
    documentai_client = documentai.DocumentProcessorServiceClient(client_options=opts)

    # The full resource name of the processor, e.g.:
    # projects/project-id/locations/location/processor/processor-id
    # You must create new processors in the Cloud Console first
    resource_name = documentai_client.processor_path(project_id, location, processor_id)

    # Read the file into memory
    with open(file_path, "rb") as image:
        image_content = image.read()

        # Load Binary Data into Document AI RawDocument Object
        raw_document = documentai.RawDocument(
            content=image_content, mime_type=mime_type
        )

        # Configure the process request
        request = documentai.ProcessRequest(
            name=resource_name, raw_document=raw_document
        )

        # Use the Document AI client to process the sample form
        result = documentai_client.process_document(request=request)

        return result.document


def get_table_data(
    rows: Sequence[documentai.Document.Page.Table.TableRow], text: str
) -> List[List[str]]:
    """
    Get Text data from table rows
    """
    all_values: List[List[str]] = []
    for row in rows:
        current_row_values: List[str] = []
        for cell in row.cells:
            current_row_values.append(
                text_anchor_to_text(cell.layout.text_anchor, text)
            )
        all_values.append(current_row_values)
    return all_values


def text_anchor_to_text(text_anchor: documentai.Document.TextAnchor, text: str) -> str:
    """
    Document AI identifies table data by their offsets in the entirety of the
    document's text. This function converts offsets to a string.
    """
    response = ""
    # If a text segment spans several lines, it will
    # be stored in different text segments.
    for segment in text_anchor.text_segments:
        start_index = int(segment.start_index)
        end_index = int(segment.end_index)
        response += text[start_index:end_index]
    return response.strip().replace("\n", " ")


PROJECT_ID = "YOUR_PROJECT_ID"
LOCATION = "YOUR_PROJECT_LOCATION"  # Format is 'us' or 'eu'
PROCESSOR_ID = "FORM_PARSER_ID"  # Create processor before running sample

# The local file in your current working directory
FILE_PATH = "form_with_tables.pdf"
# Refer to https://cloud.google.com/document-ai/docs/file-types
# for supported file types
MIME_TYPE = "application/pdf"

document = online_process(
    project_id=PROJECT_ID,
    location=LOCATION,
    processor_id=PROCESSOR_ID,
    file_path=FILE_PATH,
    mime_type=MIME_TYPE,
)

header_row_values: List[List[str]] = []
body_row_values: List[List[str]] = []

# Input Filename without extension
output_file_prefix = splitext(FILE_PATH)[0]

for page in document.pages:
    for index, table in enumerate(page.tables):
        header_row_values = get_table_data(table.header_rows, document.text)
        body_row_values = get_table_data(table.body_rows, document.text)

        # Create a Pandas Dataframe to print the values in tabular format.
        df = pd.DataFrame(
            data=body_row_values,
            columns=pd.MultiIndex.from_arrays(header_row_values),
        )

        print(f"Page {page.page_number} - Table {index}")
        print(df)

        # Save each table as a CSV file
        output_filename = f"{output_file_prefix}_pg{page.page_number}_tb{index}.csv"
        df.to_csv(output_filename, index=False)

现在运行代码,您应该会看到提取并输出的文本。

如果使用我们的示例文档,您应该会看到以下输出内容:

$ python3 table_parsing.py
Page 1 - Table 0
     Item    Description
0  Item 1  Description 1
1  Item 2  Description 2
2  Item 3  Description 3
Page 1 - Table 1
  Form Number:     12345678
0   Form Date:   2020/10/01
1        Name:   First Last
2     Address:  123 Fake St

在运行代码的目录中,您还应该有两个新的 CSV 文件。

$ ls
form_with_tables_pg1_tb0.csv form_with_tables_pg1_tb1.csv table_parsing.py

7. 恭喜

恭喜,您已成功使用 Document AI API 从手写表单中提取数据。我们建议您尝试使用其他表单文档。

清理

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请执行以下操作:

  • 在 Cloud Console 中,转到管理资源页面。
  • 在项目列表中,选择您的项目,然后点击“删除”。
  • 在对话框中输入项目 ID,然后点击“关停”以删除项目。

了解详情

通过以下后续 Codelab 继续了解 Document AI。

资源

许可

此作品已获得 Creative Commons Attribution 2.0 通用许可授权。