Trích xuất nội dung từ tài liệu Word DOCX bằng Python

Trích xuất văn bản từ tài liệu Word thường được thực hiện trong các tình huống khác nhau. Ví dụ: để phân tích văn bản, trích xuất các phần cụ thể của tài liệu và kết hợp chúng thành một tài liệu duy nhất, v.v. Trong bài viết này, bạn sẽ học cách trích xuất văn bản từ tài liệu Word theo lập trình bằng Python. Hơn nữa, chúng tôi sẽ đề cập đến cách trích xuất nội dung giữa các phần tử cụ thể như đoạn văn, bảng, v.v. một cách động.

Thư viện Python để trích xuất văn bản từ tài liệu Word

Aspose.Words for Python là một thư viện mạnh mẽ cho phép bạn tạo các tài liệu MS Word từ đầu. Hơn nữa, nó cho phép bạn thao tác các tài liệu Word hiện có để mã hóa, chuyển đổi, trích xuất văn bản, v.v. Chúng tôi sẽ sử dụng thư viện này để trích xuất văn bản từ các tài liệu Word DOCX hoặc DOC. Bạn có thể cài đặt thư viện từ PyPI bằng lệnh pip sau.

pip install aspose-words

Trích xuất văn bản trong tài liệu Word bằng Python

Một tài liệu MS Word bao gồm các phần tử khác nhau bao gồm đoạn văn, bảng, hình ảnh, v.v. Do đó, các yêu cầu trích xuất văn bản có thể khác nhau giữa các tình huống. Ví dụ: bạn có thể cần trích xuất văn bản giữa các đoạn văn, dấu trang, nhận xét, v.v.

Mỗi loại phần tử trong tài liệu Word được biểu diễn dưới dạng một nút. Do đó, để xử lý một tài liệu, bạn sẽ phải chơi với các nút. Vì vậy, hãy bắt đầu và xem cách trích xuất văn bản từ tài liệu Word trong các trường hợp khác nhau.

Trích xuất văn bản từ tài liệu Word bằng Python

Trong phần này, chúng ta sẽ triển khai trình trích xuất văn bản Python cho các tài liệu Word và quy trình trích xuất văn bản sẽ như sau:

  • Đầu tiên, chúng tôi sẽ xác định các nút mà chúng tôi muốn đưa vào quá trình trích xuất văn bản.
  • Sau đó, chúng tôi sẽ trích xuất nội dung giữa các nút được chỉ định (bao gồm hoặc loại trừ các nút bắt đầu và kết thúc).
  • Cuối cùng, chúng tôi sẽ sử dụng bản sao của các nút được trích xuất, ví dụ: để tạo một tài liệu Word mới bao gồm nội dung được trích xuất.

Bây giờ chúng ta hãy viết một phương thức có tên là extractcontent mà chúng ta sẽ truyền các nút và một số tham số khác để thực hiện trích xuất văn bản. Phương thức này sẽ phân tích cú pháp tài liệu và sao chép các nút. Sau đây là các tham số mà chúng ta sẽ truyền cho phương thức này.

  1. StartNode và EndNode lần lượt là điểm bắt đầu và điểm kết thúc cho việc trích xuất nội dung. Đây có thể là cả cấp khối (Đoạn, Bảng) hoặc cấp nội tuyến (ví dụ: Run, FieldStart, BookmarkStart, v.v.).
    1. Để chuyển một trường, bạn nên chuyển đối tượng FieldStart tương ứng.
    2. Để vượt qua dấu trang, các nút BookmarkStart và BookmarkEnd phải được chuyển.
    3. Đối với các nhận xét, các nút CommentRangeStart và CommentRangeEnd nên được sử dụng.
  2. IsInclusive xác định xem các điểm đánh dấu có được đưa vào phần trích xuất hay không. Nếu tùy chọn này được đặt thành false và cùng một nút hoặc các nút liên tiếp được chuyển, thì một danh sách trống sẽ được trả về.

Sau đây là cách triển khai hoàn chỉnh của phương thức trích xuất nội dung giữa các nút được truyền.

def extract_content(startNode : aw.Node, endNode : aw.Node, isInclusive : bool):
    
    # Đầu tiên, hãy kiểm tra xem các nút được chuyển đến phương thức này có hợp lệ để sử dụng hay không.
    verify_parameter_nodes(startNode, endNode)

    # Tạo một danh sách để lưu trữ các nút đã trích xuất.
    nodes = []

    # Nếu một trong hai điểm đánh dấu là một phần của nhận xét, bao gồm cả chính nhận xét đó, chúng ta cần di chuyển con trỏ
    # chuyển tiếp đến Nút nhận xét được tìm thấy sau nút CommentRangeEnd.
    if (endNode.node_type == aw.NodeType.COMMENT_RANGE_END and isInclusive) :
        
        node = find_next_node(aw.NodeType.COMMENT, endNode.next_sibling)
        if (node != None) :
            endNode = node

    # Giữ một bản ghi về các nút ban đầu được chuyển đến phương pháp này để tách các nút đánh dấu nếu cần.
    originalStartNode = startNode
    originalEndNode = endNode

    # Trích xuất nội dung dựa trên các nút cấp khối (đoạn văn và bảng). Đi qua các nút cha để tìm chúng.
    # Chúng tôi sẽ chia nội dung của nút đầu tiên và nút cuối cùng, tùy thuộc vào việc các nút đánh dấu có nằm trong dòng hay không.
    startNode = get_ancestor_in_body(startNode)
    endNode = get_ancestor_in_body(endNode)

    isExtracting = True
    isStartingNode = True
    # Nút hiện tại mà chúng tôi đang trích xuất từ tài liệu.
    currNode = startNode

    # Bắt đầu trích xuất nội dung. Xử lý tất cả các nút cấp khối và tách cụ thể nút đầu tiên
    # và các nút cuối cùng khi cần thiết, do đó định dạng đoạn văn được giữ lại.
    # Phương pháp phức tạp hơn một chút so với máy vắt thông thường vì chúng ta cần yếu tố
    # trong việc trích xuất bằng cách sử dụng các nút nội tuyến, trường, dấu trang, v.v. để làm cho nó hữu ích.
    while (isExtracting) :
        
        # Sao chép nút hiện tại và nút con của nó để lấy một bản sao.
        cloneNode = currNode.clone(True)
        isEndingNode = currNode == endNode

        if (isStartingNode or isEndingNode) :
            
            # Chúng tôi cần xử lý từng điểm đánh dấu riêng biệt, vì vậy thay vào đó, hãy chuyển nó sang một phương pháp riêng biệt.
            # Kết thúc nên được xử lý lúc đầu để giữ các chỉ mục của nút.
            if (isEndingNode) :
                # ! isStartingNode: không thêm nút hai lần nếu các điểm đánh dấu là cùng một nút.
                process_marker(cloneNode, nodes, originalEndNode, currNode, isInclusive, False, not isStartingNode, False)
                isExtracting = False

            # Điều kiện cần được tách biệt vì các điểm đánh dấu bắt đầu và kết thúc cấp khối, có thể là cùng một nút.
            if (isStartingNode) :
                process_marker(cloneNode, nodes, originalStartNode, currNode, isInclusive, True, True, False)
                isStartingNode = False
            
        else :
            # Node không phải là điểm đánh dấu bắt đầu hoặc kết thúc, chỉ cần thêm bản sao vào danh sách.
            nodes.append(cloneNode)

        # Di chuyển đến nút tiếp theo và giải nén nó. Nếu nút tiếp theo là Không,
        # phần còn lại của nội dung được tìm thấy trong một phần khác.
        if (currNode.next_sibling == None and isExtracting) :
            # Chuyển sang phần tiếp theo.
            nextSection = currNode.get_ancestor(aw.NodeType.SECTION).next_sibling.as_section()
            currNode = nextSection.body.first_child
            
        else :
            # Di chuyển đến nút tiếp theo trong nội dung.
            currNode = currNode.next_sibling
            
    # Để tương thích với chế độ có dấu trang nội dòng, hãy thêm đoạn tiếp theo (trống).
    if (isInclusive and originalEndNode == endNode and not originalEndNode.is_composite) :
        include_next_paragraph(endNode, nodes)

    # Trả lại các nút giữa các điểm đánh dấu nút.
    return nodes

Một số phương thức trợ giúp cũng được yêu cầu bởi phương thức extractcontent để thực hiện thao tác trích xuất văn bản, được đưa ra bên dưới.

def verify_parameter_nodes(start_node: aw.Node, end_node: aw.Node):

    # Thứ tự thực hiện các kiểm tra này là quan trọng.
    if start_node is None:
        raise ValueError("Start node cannot be None")
    if end_node is None:
        raise ValueError("End node cannot be None")

    if start_node.document != end_node.document:
        raise ValueError("Start node and end node must belong to the same document")

    if start_node.get_ancestor(aw.NodeType.BODY) is None or end_node.get_ancestor(aw.NodeType.BODY) is None:
        raise ValueError("Start node and end node must be a child or descendant of a body")

    # Kiểm tra nút kết thúc nằm sau nút bắt đầu trong cây DOM.
    # Trước tiên, hãy kiểm tra xem chúng có nằm trong các phần khác nhau hay không, sau đó nếu không,
    # kiểm tra vị trí của chúng trong phần thân của cùng một phần.
    start_section = start_node.get_ancestor(aw.NodeType.SECTION).as_section()
    end_section = end_node.get_ancestor(aw.NodeType.SECTION).as_section()

    start_index = start_section.parent_node.index_of(start_section)
    end_index = end_section.parent_node.index_of(end_section)

    if start_index == end_index:

        if (start_section.body.index_of(get_ancestor_in_body(start_node)) >
            end_section.body.index_of(get_ancestor_in_body(end_node))):
            raise ValueError("The end node must be after the start node in the body")

    elif start_index > end_index:
        raise ValueError("The section of end node must be after the section start node")

 
def find_next_node(node_type: aw.NodeType, from_node: aw.Node):

    if from_node is None or from_node.node_type == node_type:
        return from_node

    if from_node.is_composite:

        node = find_next_node(node_type, from_node.as_composite_node().first_child)
        if node is not None:
            return node

    return find_next_node(node_type, from_node.next_sibling)

 
def is_inline(node: aw.Node):

    # Kiểm tra xem nút có phải là con của nút Đoạn hoặc Bảng và không phải là một đoạn hay không
    # hoặc một bảng một đoạn bên trong một lớp bình luận là có thể có một đoạn văn.
    return ((node.get_ancestor(aw.NodeType.PARAGRAPH) is not None or node.get_ancestor(aw.NodeType.TABLE) is not None) and
            not (node.node_type == aw.NodeType.PARAGRAPH or node.node_type == aw.NodeType.TABLE))

 
def process_marker(clone_node: aw.Node, nodes, node: aw.Node, block_level_ancestor: aw.Node,
    is_inclusive: bool, is_start_marker: bool, can_add: bool, force_add: bool):

    # Nếu chúng ta đang xử lý một nút cấp khối, hãy xem liệu nó có nên được đưa vào không và thêm nó vào danh sách.
    if node == block_level_ancestor:
        if can_add and is_inclusive:
            nodes.append(clone_node)
        return

    # cloneNode là một bản sao của blockLevelNode. Nếu node! = BlockLevelNode, blockLevelAncestor
    # là tổ tiên của nút có nghĩa là nó là một nút tổng hợp.
    assert clone_node.is_composite

    # Nếu một điểm đánh dấu là một nút FieldStart, hãy kiểm tra xem nó có được đưa vào hay không.
    # Để đơn giản, chúng tôi giả định rằng FieldStart và FieldEnd xuất hiện trong cùng một đoạn.
    if node.node_type == aw.NodeType.FIELD_START:
        # Nếu điểm đánh dấu là nút bắt đầu và không được bao gồm, hãy chuyển đến cuối trường.
        # Nếu điểm đánh dấu là một nút kết thúc và sẽ được bao gồm, thì hãy di chuyển đến trường kết thúc để trường không bị xóa.
        if is_start_marker and not is_inclusive or not is_start_marker and is_inclusive:
            while node.next_sibling is not None and node.node_type != aw.NodeType.FIELD_END:
                node = node.next_sibling

    # Hỗ trợ một trường hợp nếu nút đánh dấu ở cấp thứ ba của nội dung tài liệu hoặc thấp hơn.
    node_branch = fill_self_and_parents(node, block_level_ancestor)

    # Xử lý nút tương ứng trong nút nhân bản của chúng tôi theo chỉ mục.
    current_clone_node = clone_node
   for i in range(len(node_branch) - 1, -1):

        current_node = node_branch[i]
        node_index = current_node.parent_node.index_of(current_node)
        current_clone_node = current_clone_node.as_composite_node.child_nodes[node_index]

        remove_nodes_outside_of_range(current_clone_node, is_inclusive or (i > 0), is_start_marker)

    # Sau khi xử lý, nút tổng hợp có thể trở nên trống nếu nó không bao gồm nó.
    if can_add and (force_add or clone_node.as_composite_node().has_child_nodes):
        nodes.append(clone_node)

 
def remove_nodes_outside_of_range(marker_node: aw.Node, is_inclusive: bool, is_start_marker: bool):

    is_processing = True
    is_removing = is_start_marker
    next_node = marker_node.parent_node.first_child

    while is_processing and next_node is not None:

        current_node = next_node
        is_skip = False

        if current_node == marker_node:
            if is_start_marker:
                is_processing = False
                if is_inclusive:
                    is_removing = False
            else:
                is_removing = True
                if is_inclusive:
                    is_skip = True

        next_node = next_node.next_sibling
        if is_removing and not is_skip:
            current_node.remove()

 
def fill_self_and_parents(node: aw.Node, till_node: aw.Node):

    nodes = []
    current_node = node

    while current_node != till_node:
        nodes.append(current_node)
        current_node = current_node.parent_node

    return nodes

 
def include_next_paragraph(node: aw.Node, nodes):

    paragraph = find_next_node(aw.NodeType.PARAGRAPH, node.next_sibling).as_paragraph()
    if paragraph is not None:

        # Di chuyển đến con đầu tiên để bao gồm các đoạn văn không có nội dung.
        marker_node = paragraph.first_child if paragraph.has_child_nodes else paragraph
        root_node = get_ancestor_in_body(paragraph)

        process_marker(root_node.clone(True), nodes, marker_node, root_node,
            marker_node == paragraph, False, True, True)

 
def get_ancestor_in_body(start_node: aw.Node):

    while start_node.parent_node.node_type != aw.NodeType.BODY:
        start_node = start_node.parent_node
    return start_node
def generate_document(src_doc: aw.Document, nodes):

    dst_doc = aw.Document()
    # Xóa đoạn đầu tiên khỏi tài liệu trống.
    dst_doc.first_section.body.remove_all_children()

    # Nhập từng nút từ danh sách vào tài liệu mới. Giữ nguyên định dạng ban đầu của nút.
    importer = aw.NodeImporter(src_doc, dst_doc, aw.ImportFormatMode.KEEP_SOURCE_FORMATTING)

   for node in nodes:
        import_node = importer.import_node(node, True)
        dst_doc.first_section.body.append_child(import_node)

    return dst_doc

 
def paragraphs_by_style_name(doc: aw.Document, style_name: str):

    paragraphs_with_style = []
    paragraphs = doc.get_child_nodes(aw.NodeType.PARAGRAPH, True)

   for paragraph in paragraphs:
        paragraph = paragraph.as_paragraph()
        if paragraph.paragraph_format.style.name == style_name:
            paragraphs_with_style.append(paragraph)

    return paragraphs_with_style

Bây giờ chúng tôi đã sẵn sàng sử dụng các phương pháp này và trích xuất văn bản từ tài liệu Word.

Trích xuất văn bản giữa các đoạn trong tài liệu Word

Hãy xem cách trích xuất nội dung giữa hai đoạn văn trong tài liệu Word DOCX. Sau đây là các bước để thực hiện thao tác này trong Python.

  • Đầu tiên, tải tài liệu Word bằng lớp Tài liệu.
  • Nhận tham chiếu của các đoạn văn bắt đầu và kết thúc thành hai đối tượng bằng cách sử dụng phương thức Document.firstsection.body.getchild (NodeType.PARAGRAPH, int, boolean) .asparagraph().
  • Gọi phương thức extractcontent (startPara, endPara, True) để trích xuất các nút thành một đối tượng.
  • Gọi phương thức trợ giúp createocument (Document, ExtractNodes) để tạo tài liệu bao gồm nội dung được trích xuất.
  • Cuối cùng, lưu tài liệu trả về bằng phương thức Document.save (string).

Mẫu mã sau đây cho thấy cách trích xuất văn bản giữa đoạn văn thứ 7 và 11 trong tài liệu Word bằng Python.

# Tải tài liệu.
doc = aw.Document("Extract content.docx")

# Xác định đoạn văn bắt đầu và kết thúc.
startPara = doc.first_section.body.get_child(aw.NodeType.PARAGRAPH, 6, True).as_paragraph()
endPara = doc.first_section.body.get_child(aw.NodeType.PARAGRAPH, 10, True).as_paragraph()

# Trích xuất nội dung giữa các đoạn này trong tài liệu. Bao gồm các điểm đánh dấu này trong phần trích xuất.
extractedNodes = extract_content(startPara, endPara, True)

# Tạo tài liệu có chứa nội dung được trích xuất.
dstDoc = generate_document(doc, extractedNodes)

# Lưu tài liệu.
dstDoc.save("extract_content_between_paragraphs.docx")

Trích xuất văn bản giữa các loại nút khác nhau trong tài liệu Word

Bạn cũng có thể trích xuất nội dung giữa các loại nút khác nhau. Để minh họa, hãy trích xuất nội dung giữa một đoạn văn và một bảng và lưu nó vào một tài liệu Word mới. Sau đây là các bước để thực hiện thao tác này.

  • Tải tài liệu Word bằng lớp Tài liệu.
  • Nhận tham chiếu của các nút bắt đầu và kết thúc thành hai đối tượng bằng cách sử dụng phương thức Document.firstsection.body.getchild (NodeType, int, boolean).
  • Gọi phương thức extractcontent (startPara, endPara, True) để trích xuất các nút thành một đối tượng.
  • Gọi phương thức trợ giúp createocument (Document, ExtractNodes) để tạo tài liệu bao gồm nội dung được trích xuất.
  • Lưu tài liệu đã trả về bằng phương thức Document.save (string).

Mẫu mã sau đây cho thấy cách trích xuất văn bản giữa một đoạn văn và một bảng trong Python.

# Tải tài liệu
doc = aw.Document("Extract content.docx")

# Xác định các nút bắt đầu và kết thúc.
start_para = doc.last_section.get_child(aw.NodeType.PARAGRAPH, 2, True).as_paragraph()
end_table = doc.last_section.get_child(aw.NodeType.TABLE, 0, True).as_table()

# Trích xuất nội dung giữa các nút này trong tài liệu. Bao gồm các điểm đánh dấu này trong phần trích xuất.
extracted_nodes = extract_content(start_para, end_table, True)

# Tạo tài liệu có chứa nội dung được trích xuất.
dstDoc = generate_document(doc, extractedNodes)

# Lưu tài liệu.
dstDoc.save("extract_content_between_nodes.docx")

Trích xuất Văn bản giữa các Đoạn văn dựa trên Kiểu

Bây giờ chúng ta hãy kiểm tra cách trích xuất nội dung giữa các đoạn văn dựa trên phong cách. Để minh họa, chúng tôi sẽ trích xuất nội dung giữa “Tiêu đề 1” đầu tiên và “Tiêu đề 3” đầu tiên trong tài liệu Word. Các bước sau đây trình bày cách đạt được điều này trong Python.

  • Đầu tiên, tải tài liệu Word bằng lớp Tài liệu.
  • Sau đó, trích xuất các đoạn văn vào một đối tượng bằng cách sử dụng phương thức trợ giúp paragraphsbystylename (Document, “Heading 1”).
  • Trích xuất các đoạn văn vào một đối tượng khác bằng cách sử dụng phương thức trợ giúp paragraphsbystylename (Document, “Heading 3”).
  • Gọi phương thức extractcontent (startPara, endPara, True) và chuyển các phần tử đầu tiên trong cả hai mảng đoạn làm tham số đầu tiên và thứ hai.
  • Gọi phương thức trợ giúp createocument (Document, ExtractNodes) để tạo tài liệu bao gồm nội dung được trích xuất.
  • Cuối cùng, lưu tài liệu trả về bằng phương thức Document.save (string).

Mẫu mã sau đây cho thấy cách trích xuất nội dung giữa các đoạn văn dựa trên kiểu.

# Tải tài liệu
doc = aw.Document("Extract content.docx")

# Tập hợp danh sách các đoạn văn bằng cách sử dụng các kiểu tiêu đề tương ứng.
parasStyleHeading1 = paragraphs_by_style_name(doc, "Heading 1")
parasStyleHeading3 = paragraphs_by_style_name(doc, "Heading 3")

# Sử dụng phiên bản đầu tiên của các đoạn văn với các phong cách đó.
startPara1 = parasStyleHeading1[0]
endPara1 = parasStyleHeading3[0]

# Trích xuất nội dung giữa các nút này trong tài liệu. Không bao gồm các điểm đánh dấu này trong phần trích xuất.
extractedNodes = extract_content(startPara1, endPara1, False)

# Tạo tài liệu có chứa nội dung được trích xuất.
dstDoc = generate_document(doc, extractedNodes)

# Lưu tài liệu.
dstDoc.save("extract_content_between_paragraphs_based_on-Styles.docx")

Đọc thêm

Bạn có thể khám phá các trường hợp trích xuất văn bản từ tài liệu Word khác bằng cách sử dụng bài viết tài liệu này.

Nhận giấy phép API miễn phí

Bạn có thể nhận giấy phép tạm thời để sử dụng Aspose.Words dành cho Python mà không có giới hạn đánh giá.

Sự kết luận

Trong bài viết này, bạn đã học cách trích xuất văn bản từ tài liệu MS Word bằng Python. Hơn nữa, bạn đã thấy cách trích xuất nội dung giữa các loại nút tương tự hoặc khác nhau trong tài liệu Word theo chương trình. Do đó, bạn có thể xây dựng trình trích xuất văn bản MS Word của riêng mình bằng Python. Ngoài ra, bạn có thể khám phá các tính năng khác của Aspose. AdWords for Python bằng cách sử dụng tài liệu. Trong trường hợp bạn có bất kỳ câu hỏi nào, vui lòng cho chúng tôi biết qua diễn đàn của chúng tôi.

Xem thêm

Thông tin: Nếu bạn cần lấy tài liệu Word từ bản trình bày PowerPoint, bạn có thể sử dụng trình chuyển đổi Aspose Trình bày sang Tài liệu Word.