Extrahieren Sie Inhalte aus Word-DOCX Dokumenten in Python

Die Textextraktion aus Word Dokumenten wird häufig in verschiedenen Szenarien durchgeführt. Zum Beispiel, um den Text zu analysieren, bestimmte Abschnitte eines Dokuments zu extrahieren und sie zu einem einzigen Dokument zusammenzufügen und so weiter. In diesem Artikel erfahren Sie, wie Sie Text aus Word Dokumenten programmgesteuert in Python extrahieren. Darüber hinaus werden wir behandeln, wie Inhalte zwischen bestimmten Elementen wie Absätzen, Tabellen usw. dynamisch extrahiert werden.

Python Bibliothek zum Extrahieren von Text aus Word Dokumenten

Aspose.Words for Python ist eine leistungsstarke Bibliothek, mit der Sie MS Word Dokumente von Grund auf neu erstellen können. Darüber hinaus können Sie die vorhandenen Word Dokumente für Verschlüsselung, Konvertierung, Textextraktion usw. manipulieren. Wir werden diese Bibliothek verwenden, um Text aus den Word DOCX oder DOC Dokumenten zu extrahieren. Sie können die Bibliothek von PyPI mit dem folgenden Pip Befehl installieren.

pip install aspose-words

Textextraktion in Word Dokumenten mit Python

Ein MS Word Dokument besteht aus verschiedenen Elementen, darunter Absätze, Tabellen, Bilder usw. Daher können die Anforderungen an die Textextraktion von Szenario zu Szenario variieren. Beispielsweise müssen Sie möglicherweise Text zwischen Absätzen, Lesezeichen, Kommentaren usw. extrahieren.

Jeder Elementtyp in einem Word Dokument wird als Knoten dargestellt. Um ein Dokument zu verarbeiten, müssen Sie daher mit den Knoten spielen. Lassen Sie uns also beginnen und sehen, wie Sie Text aus Word Dokumenten in verschiedenen Szenarien extrahieren.

Extrahieren Sie Text aus einem Word Dokument in Python

In diesem Abschnitt werden wir einen Python-Textextraktor für Word Dokumente implementieren, und der Arbeitsablauf der Textextraktion wäre wie folgt:

  • Zuerst definieren wir die Knoten, die wir in den Textextraktionsprozess einbeziehen möchten.
  • Dann extrahieren wir den Inhalt zwischen den angegebenen Knoten (einschließlich oder ohne Start und Endknoten).
  • Schließlich verwenden wir den Klon extrahierter Knoten, um zB ein neues Word Dokument zu erstellen, das aus extrahierten Inhalten besteht.

Lassen Sie uns nun eine Methode namens extract\content schreiben, an die wir die Knoten und einige andere Parameter übergeben, um die Textextraktion durchzuführen. Diese Methode analysiert das Dokument und klont die Knoten. Im Folgenden sind die Parameter aufgeführt, die wir an diese Methode übergeben.

  1. StartNode und EndNode als Start- bzw. Endpunkt für die Extraktion des Inhalts. Dies können sowohl Knoten auf Blockebene (Absatz , Tabelle) als auch auf Inline-Ebene (z. B. Run, FieldStart, BookmarkStart usw.) sein.
    1. Um ein Feld zu übergeben, sollten Sie das entsprechende FieldStart objekt übergeben.
    2. Um Lesezeichen zu übergeben, sollten die Knoten BookmarkStart und BookmarkEnd übergeben werden.
    3. Für Kommentare sollten die Knoten CommentRangeStart und CommentRangeEnd verwendet werden.
  2. IsInclusive definiert, ob die Marker in der Extraktion enthalten sind oder nicht. Wenn diese Option auf false gesetzt ist und derselbe Knoten oder aufeinanderfolgende Knoten übergeben werden, wird eine leere Liste zurückgegeben.

Das Folgende ist die vollständige Implementierung der Methode extract\content, die den Inhalt zwischen den übergebenen Knoten extrahiert.

def extract_content(startNode : aw.Node, endNode : aw.Node, isInclusive : bool):
    
    # First, check that the nodes passed to this method are valid for use.
    verify_parameter_nodes(startNode, endNode)

    # Create a list to store the extracted nodes.
    nodes = []

    # If either marker is part of a comment, including the comment itself, we need to move the pointer
    # forward to the Comment Node found after the CommentRangeEnd node.
    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

    # Keep a record of the original nodes passed to this method to split marker nodes if needed.
    originalStartNode = startNode
    originalEndNode = endNode

    # Extract content based on block-level nodes (paragraphs and tables). Traverse through parent nodes to find them.
    # We will split the first and last nodes' content, depending if the marker nodes are inline.
    startNode = get_ancestor_in_body(startNode)
    endNode = get_ancestor_in_body(endNode)

    isExtracting = True
    isStartingNode = True
    # The current node we are extracting from the document.
    currNode = startNode

    # Begin extracting content. Process all block-level nodes and specifically split the first
    # and last nodes when needed, so paragraph formatting is retained.
    # Method is a little more complicated than a regular extractor as we need to factor
    # in extracting using inline nodes, fields, bookmarks, etc. to make it useful.
    while (isExtracting) :
        
        # Clone the current node and its children to obtain a copy.
        cloneNode = currNode.clone(True)
        isEndingNode = currNode == endNode

        if (isStartingNode or isEndingNode) :
            
            # We need to process each marker separately, so pass it off to a separate method instead.
            # End should be processed at first to keep node indexes.
            if (isEndingNode) :
                # !isStartingNode: don't add the node twice if the markers are the same node.
                process_marker(cloneNode, nodes, originalEndNode, currNode, isInclusive, False, not isStartingNode, False)
                isExtracting = False

            # Conditional needs to be separate as the block level start and end markers, maybe the same node.
            if (isStartingNode) :
                process_marker(cloneNode, nodes, originalStartNode, currNode, isInclusive, True, True, False)
                isStartingNode = False
            
        else :
            # Node is not a start or end marker, simply add the copy to the list.
            nodes.append(cloneNode)

        # Move to the next node and extract it. If the next node is None,
        # the rest of the content is found in a different section.
        if (currNode.next_sibling == None and isExtracting) :
            # Move to the next section.
            nextSection = currNode.get_ancestor(aw.NodeType.SECTION).next_sibling.as_section()
            currNode = nextSection.body.first_child
            
        else :
            # Move to the next node in the body.
            currNode = currNode.next_sibling
            
    # For compatibility with mode with inline bookmarks, add the next paragraph (empty).
    if (isInclusive and originalEndNode == endNode and not originalEndNode.is_composite) :
        include_next_paragraph(endNode, nodes)

    # Return the nodes between the node markers.
    return nodes

Einige Hilfsmethoden werden auch von der Methode extract\content benötigt, um die Textextraktionsoperation durchzuführen, die unten angegeben ist.

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

    # The order in which these checks are done is important.
    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")

    # Check the end node is after the start node in the DOM tree.
    # First, check if they are in different sections, then if they're not,
    # check their position in the body of the same section.
    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):

    # Test if the node is a descendant of a Paragraph or Table node and is not a paragraph
    # or a table a paragraph inside a comment class that is decent of a paragraph is possible.
    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):

    # If we are dealing with a block-level node, see if it should be included and add it to the list.
    if node == block_level_ancestor:
        if can_add and is_inclusive:
            nodes.append(clone_node)
        return

    # cloneNode is a clone of blockLevelNode. If node != blockLevelNode, blockLevelAncestor
    # is the node's ancestor that means it is a composite node.
    assert clone_node.is_composite

    # If a marker is a FieldStart node check if it's to be included or not.
    # We assume for simplicity that the FieldStart and FieldEnd appear in the same paragraph.
    if node.node_type == aw.NodeType.FIELD_START:
        # If the marker is a start node and is not included, skip to the end of the field.
        # If the marker is an end node and is to be included, then move to the end field so the field will not be removed.
        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

    # Support a case if the marker node is on the third level of the document body or lower.
    node_branch = fill_self_and_parents(node, block_level_ancestor)

    # Process the corresponding node in our cloned node by index.
    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)

    # After processing, the composite node may become empty if it has doesn't include it.
    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:

        # Move to the first child to include paragraphs without content.
        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()
    # Remove the first paragraph from the empty document.
    dst_doc.first_section.body.remove_all_children()

    # Import each node from the list into the new document. Keep the original formatting of the node.
    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

Jetzt sind wir bereit, diese Methoden zu nutzen und Text aus einem Word Dokument zu extrahieren.

Extrahieren Sie Text zwischen Absätzen in einem Word Dokument

Sehen wir uns an, wie man Inhalte zwischen zwei Absätzen in einem Word-DOCX-Dokument extrahiert. Im Folgenden sind die Schritte zum Ausführen dieses Vorgangs in Python aufgeführt.

  • Laden Sie zuerst das Word Dokument mit der Document Klasse.
  • Rufen Sie mithilfe der Methode Document.first\section.body.get\child(NodeType.PARAGRAPH, int, boolean).as\paragraph() die Referenz der Anfangs und Endabsätze in zwei Objekte ab.
  • Rufen Sie die Methode extract\content(startPara, endPara, True) auf, um die Knoten in ein Objekt zu extrahieren.
  • Rufen Sie die Hilfsmethode generate\document(Document, extractedNodes) auf, um ein Dokument zu erstellen, das aus dem extrahierten Inhalt besteht.
  • Speichern Sie abschließend das zurückgegebene Dokument mit der methode Document.save(string).

Das folgende Codebeispiel zeigt, wie Text zwischen dem 7. und 11. Absatz in einem Word Dokument in Python extrahiert wird.

# Load document.
doc = aw.Document("Extract content.docx")

# Define starting and ending paragraphs.
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()

# Extract the content between these paragraphs in the document. Include these markers in the extraction.
extractedNodes = extract_content(startPara, endPara, True)

# Generate document containing extracted content.
dstDoc = generate_document(doc, extractedNodes)

# Save document.
dstDoc.save("extract_content_between_paragraphs.docx")

Extrahieren Sie Text zwischen verschiedenen Arten von Knoten in einem Word Dokument

Sie können auch Inhalte zwischen verschiedenen Arten von Knoten extrahieren. Lassen Sie uns zur Demonstration den Inhalt zwischen einem Absatz und einer Tabelle extrahieren und in einem neuen Word Dokument speichern. Im Folgenden sind die Schritte zum Ausführen dieses Vorgangs aufgeführt.

  • Laden Sie das Word Dokument mithilfe der Document Klasse.
  • Rufen Sie mithilfe der Methode Document.first\section.body.get\child(NodeType, int, boolean) die Referenz der Start und Endknoten in zwei Objekte ab.
  • Rufen Sie die Methode extract\content(startPara, endPara, True) auf, um die Knoten in ein Objekt zu extrahieren.
  • Rufen Sie die Hilfsmethode generate\document(Document, extractedNodes) auf, um ein Dokument zu erstellen, das aus dem extrahierten Inhalt besteht.
  • Speichern Sie das zurückgegebene Dokument mit der methode Document.save(string).

Das folgende Codebeispiel zeigt, wie Text zwischen einem Absatz und einer Tabelle in Python extrahiert wird.

# Load document
doc = aw.Document("Extract content.docx")

# Define starting and ending nodes.
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()

# Extract the content between these nodes in the document. Include these markers in the extraction.
extracted_nodes = extract_content(start_para, end_table, True)

# Generate document containing extracted content.
dstDoc = generate_document(doc, extractedNodes)

# Save document.
dstDoc.save("extract_content_between_nodes.docx")

Extrahieren Sie Text zwischen Absätzen basierend auf Stilen

Schauen wir uns nun an, wie Sie Inhalte zwischen Absätzen basierend auf Stilen extrahieren. Zur Demonstration extrahieren wir den Inhalt zwischen der ersten „Überschrift 1“ und der ersten „Überschrift 3“ im Word Dokument. Die folgenden Schritte zeigen, wie Sie dies in Python erreichen.

  • Laden Sie zuerst das Word Dokument mit der Document Klasse.
  • Extrahieren Sie dann Absätze mithilfe der Hilfsmethode Absätze\nach\Stil\Name(Dokument, “Überschrift 1”) in ein Objekt.
  • Extrahieren Sie Absätze mithilfe der Hilfsmethode Absätze\nach\Stil\Name(Dokument, “Überschrift 3”) in ein anderes Objekt.
  • Rufen Sie die Methode extract\content(startPara, endPara, True) auf und übergeben Sie die ersten Elemente in beiden Absatzarrays als ersten und zweiten Parameter.
  • Rufen Sie die Hilfsmethode generate\document(Document, extractedNodes) auf, um ein Dokument zu erstellen, das aus dem extrahierten Inhalt besteht.
  • Speichern Sie abschließend das zurückgegebene Dokument mit der methode Document.save(string).

Das folgende Codebeispiel zeigt, wie Inhalte zwischen Absätzen basierend auf Stilen extrahiert werden.

# Load document
doc = aw.Document("Extract content.docx")

# Gather a list of the paragraphs using the respective heading styles.
parasStyleHeading1 = paragraphs_by_style_name(doc, "Heading 1")
parasStyleHeading3 = paragraphs_by_style_name(doc, "Heading 3")

# Use the first instance of the paragraphs with those styles.
startPara1 = parasStyleHeading1[0]
endPara1 = parasStyleHeading3[0]

# Extract the content between these nodes in the document. Don't include these markers in the extraction.
extractedNodes = extract_content(startPara1, endPara1, False)

# Generate document containing extracted content.
dstDoc = generate_document(doc, extractedNodes)

# Save document.
dstDoc.save("extract_content_between_paragraphs_based_on-Styles.docx")

Weiterlesen

In diesem Dokumentation artikel können Sie andere Szenarien zum Extrahieren von Text aus Word Dokumenten erkunden.

Holen Sie sich eine kostenlose API Lizenz

Sie können eine vorübergehende Lizenz erwerben, um Aspose.Words for Python ohne Evaluierungseinschränkungen zu verwenden.

Fazit

In diesem Artikel haben Sie gelernt, wie Sie mit Python Text aus MS Word Dokumenten extrahieren. Darüber hinaus haben Sie gesehen, wie Inhalte zwischen ähnlichen oder unterschiedlichen Knotentypen in einem Word Dokument programmgesteuert extrahiert werden. So können Sie Ihren eigenen MS Word-Textextraktor in Python erstellen. Außerdem können Sie weitere Funktionen von Aspose.Words for Python mithilfe der Dokumentation erkunden. Falls Sie Fragen haben, können Sie uns diese gerne über unser Forum mitteilen.

Siehe auch

Info: Wenn Sie jemals ein Word Dokument aus einer PowerPoint Präsentation erhalten müssen, können Sie den Konverter Aspose Presentation to Word Document verwenden.