Extrahera innehåll från Word DOCX-dokument i Python

Textextraktion från Word-dokument utförs ofta i olika scenarier. Till exempel för att analysera texten, extrahera vissa delar av ett dokument och kombinera dem till ett enda dokument, och så vidare. I den här artikeln kommer du att lära dig hur du extraherar text från Word-dokument programmatiskt i Python. Dessutom kommer vi att täcka hur man extraherar innehåll mellan specifika element som stycken, tabeller etc. dynamiskt.

Python-bibliotek för att extrahera text från Word-dokument

Aspose.Words for Python är ett kraftfullt bibliotek som låter dig skapa MS Word-dokument från grunden. Dessutom låter det dig manipulera befintliga Word-dokument för kryptering, konvertering, textextraktion, etc. Vi kommer att använda detta bibliotek för att extrahera text från Word DOCX- eller DOC-dokument. Du kan installera biblioteket från PyPI med följande pip-kommando.

pip install aspose-words

Textextraktion i Word-dokument med Python

Ett MS Word-dokument består av olika element som inkluderar stycken, tabeller, bilder etc. Därför kan kraven på textextraktion variera från ett scenario till ett annat. Du kan till exempel behöva extrahera text mellan stycken, bokmärken, kommentarer osv.

Varje typ av element i ett Word-dokument representeras som en nod. Därför, för att bearbeta ett dokument, måste du leka med noderna. Så låt oss börja och se hur man extraherar text från Word-dokument i olika scenarier.

Extrahera text från ett Word-dokument i Python

I det här avsnittet kommer vi att implementera en Python-textextraktor för Word-dokument och arbetsflödet för textextraktion skulle vara som följer:

  • Först kommer vi att definiera de noder som vi vill inkludera i textextraktionsprocessen.
  • Sedan extraherar vi innehållet mellan de angivna noderna (inklusive eller exkluderar start- och slutnoderna).
  • Slutligen kommer vi att använda klonen av extraherade noder, till exempel för att skapa ett nytt Word-dokument som består av extraherat innehåll.

Låt oss nu skriva en metod som heter extrahera innehåll som vi skickar noderna och några andra parametrar till för att utföra textextraktionen. Denna metod kommer att analysera dokumentet och klona noderna. Följande är parametrarna som vi kommer att skicka till denna metod.

  1. StartNode och EndNode som start- och slutpunkter för utvinningen av innehållet. Dessa kan vara noder på både blocknivå (Paragraph , Table) eller inlinenivå (t.ex. Run, FieldStart, BookmarkStart etc.).
    1. För att skicka ett fält måste du skicka det motsvarande FieldStart-objektet.
    2. För att skicka bokmärken ska noderna BookmarkStart och BookmarkEnd passeras.
    3. För kommentarer ska noderna CommentRangeStart och CommentRangeEnd användas.
  2. IsInclusive definierar om markörerna ingår i extraktionen eller inte. Om det här alternativet är inställt på false och samma nod eller på varandra följande noder skickas, kommer en tom lista att returneras.

Följande är den fullständiga implementeringen av metoden extrahera innehåll som extraherar innehållet mellan noderna som skickas.

def extract_content(startNode : aw.Node, endNode : aw.Node, isInclusive : bool):
    
    # Kontrollera först att noderna som skickas till denna metod är giltiga för användning.
    verify_parameter_nodes(startNode, endNode)

    # Skapa en lista för att lagra de extraherade noderna.
    nodes = []

    # Om någon av markörerna är en del av en kommentar, inklusive själva kommentaren, måste vi flytta pekaren
    # vidarebefordra till kommentarnoden som hittas efter noden 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

    # Håll ett register över de ursprungliga noderna som skickats till denna metod för att dela markörnoder om det behövs.
    originalStartNode = startNode
    originalEndNode = endNode

    # Extrahera innehåll baserat på noder på blocknivå (stycken och tabeller). Gå igenom föräldranoder för att hitta dem.
    # Vi kommer att dela upp den första och sista nodens innehåll, beroende på om markörnoderna är inline.
    startNode = get_ancestor_in_body(startNode)
    endNode = get_ancestor_in_body(endNode)

    isExtracting = True
    isStartingNode = True
    # Den aktuella noden vi extraherar från dokumentet.
    currNode = startNode

    # Börja extrahera innehåll. Bearbeta alla noder på blocknivå och dela specifikt den första
    # och sista noder vid behov, så styckeformateringen behålls.
    # Metoden är lite mer komplicerad än en vanlig extraktor eftersom vi måste faktorisera
    # att extrahera med hjälp av inline-noder, fält, bokmärken, etc. för att göra det användbart.
    while (isExtracting) :
        
        # Klona den aktuella noden och dess underordnade för att få en kopia.
        cloneNode = currNode.clone(True)
        isEndingNode = currNode == endNode

        if (isStartingNode or isEndingNode) :
            
            # Vi måste bearbeta varje markör separat, så skicka den vidare till en separat metod istället.
            # Slutet bör bearbetas först för att behålla nodindex.
            if (isEndingNode) :
                # !isStartingNode: lägg inte till noden två gånger om markörerna är samma nod.
                process_marker(cloneNode, nodes, originalEndNode, currNode, isInclusive, False, not isStartingNode, False)
                isExtracting = False

            # Villkor måste vara separata som start- och slutmarkörer på blocknivå, kanske samma nod.
            if (isStartingNode) :
                process_marker(cloneNode, nodes, originalStartNode, currNode, isInclusive, True, True, False)
                isStartingNode = False
            
        else :
            # Noden är inte en start- eller slutmarkör, lägg bara till kopian i listan.
            nodes.append(cloneNode)

        # Flytta till nästa nod och extrahera den. Om nästa nod är Ingen,
        # resten av innehållet finns i ett annat avsnitt.
        if (currNode.next_sibling == None and isExtracting) :
            # Flytta till nästa avsnitt.
            nextSection = currNode.get_ancestor(aw.NodeType.SECTION).next_sibling.as_section()
            currNode = nextSection.body.first_child
            
        else :
            # Flytta till nästa nod i kroppen.
            currNode = currNode.next_sibling
            
    # För kompatibilitet med läge med inline-bokmärken, lägg till nästa stycke (tomt).
    if (isInclusive and originalEndNode == endNode and not originalEndNode.is_composite) :
        include_next_paragraph(endNode, nodes)

    # Returnera noderna mellan nodmarkörerna.
    return nodes

Vissa hjälpmetoder krävs också av extrahera innehållsmetoden för att utföra textextraktionsoperationen, som ges nedan.

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

    # Ordningen i vilken dessa kontroller görs är viktig.
    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")

    # Kontrollera att slutnoden ligger efter startnoden i DOM-trädet.
    # Kontrollera först om de finns i olika sektioner, sedan om de inte är det,
    # kontrollera deras position i kroppen av samma sektion.
    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):

    # Testa om noden är en avkomling av en paragraf- eller tabellnod och inte är ett stycke
    # eller en tabell ett stycke i en kommentarklass som är anständigt till ett stycke är möjligt.
    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):

    # Om vi har att göra med en nod på blocknivå, se om den ska inkluderas och lägg till den i listan.
    if node == block_level_ancestor:
        if can_add and is_inclusive:
            nodes.append(clone_node)
        return

    # cloneNode är en klon av blockLevelNode. Om nod != blockLevelNode, blockLevelAncestor
    # är nodens förfader vilket betyder att det är en sammansatt nod.
    assert clone_node.is_composite

    # Om en markör är en FieldStart-nod kontrollera om den ska inkluderas eller inte.
    # Vi antar för enkelhetens skull att FieldStart och FieldEnd visas i samma stycke.
    if node.node_type == aw.NodeType.FIELD_START:
        # Om markören är en startnod och inte ingår, hoppa till slutet av fältet.
        # Om markören är en slutnod och ska inkluderas, flytta till slutfältet så att fältet inte tas bort.
        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

    # Stöd ett fodral om markörnoden är på den tredje nivån av dokumentkroppen eller lägre.
    node_branch = fill_self_and_parents(node, block_level_ancestor)

    # Bearbeta motsvarande nod i vår klonade nod efter 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)

    # Efter bearbetning kan den sammansatta noden bli tom om den inte har inkluderat den.
    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:

        # Flytta till det första barnet för att inkludera stycken utan innehåll.
        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()
    # Ta bort första stycket från det tomma dokumentet.
    dst_doc.first_section.body.remove_all_children()

    # Importera varje nod från listan till det nya dokumentet. Behåll nodens ursprungliga formatering.
    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

Nu är vi redo att använda dessa metoder och extrahera text från ett Word-dokument.

Extrahera text mellan stycken i ett Word-dokument

Låt oss se hur man extraherar innehåll mellan två stycken i ett Word DOCX-dokument. Följande är stegen för att utföra denna operation i Python.

  • Ladda först Word-dokumentet med Document class.
  • Hämta referens till start- och slutstyckena till två objekt med metoden Document.firstsection.body.getchild(NodeType.PARAGRAPH, int, boolean).asparagraph().
  • Anropa metoden extractcontent(startPara, endPara, True) för att extrahera noderna till ett objekt.
  • Anrop genereringsdokument(Document, extractedNodes) hjälparmetod för att skapa dokument som består av det extraherade innehållet.
  • Slutligen sparar du det returnerade dokumentet med metoden Document.save(string).

Följande kodexempel visar hur man extraherar text mellan 7:e och 11:e styckena i ett Word-dokument i Python.

# Ladda dokument.
doc = aw.Document("Extract content.docx")

# Definiera start- och slutstycken.
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()

# Extrahera innehållet mellan dessa stycken i dokumentet. Inkludera dessa markörer i extraktionen.
extractedNodes = extract_content(startPara, endPara, True)

# Skapa dokument som innehåller extraherat innehåll.
dstDoc = generate_document(doc, extractedNodes)

# Spara dokument.
dstDoc.save("extract_content_between_paragraphs.docx")

Extrahera text mellan olika typer av noder i ett Word-dokument

Du kan även extrahera innehåll mellan olika typer av noder. För demonstration, låt oss extrahera innehåll mellan ett stycke och en tabell och spara det i ett nytt Word-dokument. Följande är stegen för att utföra denna operation.

  • Ladda Word-dokumentet med dokumentklassen.
  • Hämta referens för start- och slutnoderna till två objekt med metoden Document.firstsection.body.getchild(NodeType, int, boolean).
  • Anropa metoden extractcontent(startPara, endPara, True) för att extrahera noderna till ett objekt.
  • Anrop genereringsdokument(Document, extractedNodes) hjälparmetod för att skapa dokument som består av det extraherade innehållet.
  • Spara det returnerade dokumentet med metoden Document.save(string).

Följande kodexempel visar hur man extraherar text mellan ett stycke och en tabell i Python.

# Ladda dokument
doc = aw.Document("Extract content.docx")

# Definiera start- och slutnoder.
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()

# Extrahera innehållet mellan dessa noder i dokumentet. Inkludera dessa markörer i extraktionen.
extracted_nodes = extract_content(start_para, end_table, True)

# Skapa dokument som innehåller extraherat innehåll.
dstDoc = generate_document(doc, extractedNodes)

# Spara dokument.
dstDoc.save("extract_content_between_nodes.docx")

Extrahera text mellan stycken baserat på stilar

Låt oss nu kolla in hur man extraherar innehåll mellan stycken baserat på stilar. För demonstration kommer vi att extrahera innehåll mellan den första “Rubrik 1” och den första “Rubrik 3” i Word-dokumentet. Följande steg visar hur man uppnår detta i Python.

  • Ladda först Word-dokumentet med Document class.
  • Extrahera sedan stycken till ett objekt med hjälp av hjälpmetoden paragraphsbystylename(Document, “Rubrik 1”).
  • Extrahera stycken till ett annat objekt med hjälp av hjälpmetoden paragraphsbystylename(Document, “Heading 3”).
  • Anropa metoden extractcontent(startPara, endPara, True) och skicka de första elementen i båda paragrafmatriserna som första och andra parametrar.
  • Anrop genereringsdokument(Document, extractedNodes) hjälparmetod för att skapa dokument som består av det extraherade innehållet.
  • Slutligen sparar du det returnerade dokumentet med metoden Document.save(string).

Följande kodexempel visar hur man extraherar innehåll mellan stycken baserat på stilar.

# Ladda dokument
doc = aw.Document("Extract content.docx")

# Samla en lista över styckena med respektive rubrikstil.
parasStyleHeading1 = paragraphs_by_style_name(doc, "Heading 1")
parasStyleHeading3 = paragraphs_by_style_name(doc, "Heading 3")

# Använd den första instansen av styckena med dessa formatmallar.
startPara1 = parasStyleHeading1[0]
endPara1 = parasStyleHeading3[0]

# Extrahera innehållet mellan dessa noder i dokumentet. Inkludera inte dessa markörer i extraktionen.
extractedNodes = extract_content(startPara1, endPara1, False)

# Skapa dokument som innehåller extraherat innehåll.
dstDoc = generate_document(doc, extractedNodes)

# Spara dokument.
dstDoc.save("extract_content_between_paragraphs_based_on-Styles.docx")

Läs mer om textextraktion

Du kan utforska andra scenarier för att extrahera text från Word-dokument med hjälp av denna dokumentationsartikeln.

Extrahera text från Word-dokument gratis

Du kan få en gratis tillfällig licens för att extrahera text från Word-dokument utan utvärderingsbegränsningar.

Slutsats

I den här artikeln har du lärt dig hur du extraherar text från MS Word-dokument med Python. Dessutom har du sett hur man extraherar innehåll mellan liknande eller olika typer av noder i ett Word-dokument programmatiskt. Således kan du bygga din egen MS Word-textextraktor i Python. Dessutom kan du utforska andra funktioner i Aspose.Words for Python med hjälp av dokumentationen. Om du har några frågor är du välkommen att meddela oss via vårt forum.

Se även