Extrahujte obsah z dokumentů Word DOCX v Python

Extrakce textu z dokumentů aplikace Word se často provádí v různých scénářích. Například analyzovat text, extrahovat určité části dokumentu a spojit je do jednoho dokumentu a tak dále. V tomto článku se dozvíte, jak extrahovat text z dokumentů Wordu programově v Python. Kromě toho se budeme zabývat tím, jak dynamicky extrahovat obsah mezi konkrétními prvky, jako jsou odstavce, tabulky atd.

Knihovna Python pro extrahování textu z dokumentů aplikace Word

Aspose.Words for Python je výkonná knihovna, která vám umožňuje vytvářet dokumenty MS Word od začátku. Navíc vám umožňuje manipulovat se stávajícími dokumenty Wordu pro šifrování, konverzi, extrakci textu atd. Tuto knihovnu použijeme k extrahování textu z dokumentů Word DOCX nebo DOC. Knihovnu můžete nainstalovat z PyPI pomocí následujícího příkazu pip.

pip install aspose-words

Extrakce textu v dokumentech Word pomocí Python

Dokument MS Word se skládá z různých prvků, které zahrnují odstavce, tabulky, obrázky atd. Proto se požadavky na extrakci textu mohou v jednotlivých scénářích lišit. Můžete například potřebovat extrahovat text mezi odstavci, záložkami, komentáři atd.

Každý typ prvku v dokumentu aplikace Word je reprezentován jako uzel. Pro zpracování dokumentu si tedy budete muset pohrát s uzly. Začněme a podívejme se, jak extrahovat text z dokumentů aplikace Word v různých scénářích.

Extrahujte text z dokumentu aplikace Word v Python

V této části se chystáme implementovat extraktor textu Python pro dokumenty Word a pracovní postup extrakce textu by byl následující:

  • Nejprve definujeme uzly, které chceme zahrnout do procesu extrakce textu.
  • Potom extrahujeme obsah mezi zadanými uzly (včetně nebo vyloučení počátečního a koncového uzlu).
  • Nakonec použijeme klon extrahovaných uzlů, např. k vytvoření nového dokumentu Word sestávajícího z extrahovaného obsahu.

Pojďme si nyní napsat metodu s názvem extractcontent, které předáme uzly a některé další parametry pro provedení extrakce textu. Tato metoda analyzuje dokument a klonuje uzly. Následují parametry, které této metodě předáme.

  1. StartNode a EndNode jako počáteční a koncové body pro extrakci obsahu. Mohou to být uzly na úrovni bloku (Paragraph , Table) nebo inline (např. Run, FieldStart, BookmarkStart atd.).
    1. Chcete-li předat pole, měli byste předat odpovídající objekt FieldStart.
    2. Pro předání záložek by měly být předány uzly BookmarkStart a BookmarkEnd.
    3. Pro komentáře by měly být použity uzly CommentRangeStart a CommentRangeEnd.
  2. IsInclusive definuje, zda jsou značky zahrnuty do extrakce nebo ne. Pokud je tato možnost nastavena na hodnotu false a je předán stejný uzel nebo po sobě jdoucí uzly, bude vrácen prázdný seznam.

Následuje kompletní implementace metody extractcontent, která extrahuje obsah mezi uzly, které jsou předány.

def extract_content(startNode : aw.Node, endNode : aw.Node, isInclusive : bool):
    
    # Nejprve zkontrolujte, zda jsou uzly předané této metodě platné pro použití.
    verify_parameter_nodes(startNode, endNode)

    # Vytvořte seznam pro uložení extrahovaných uzlů.
    nodes = []

    # Pokud je kterákoli značka součástí komentáře, včetně komentáře samotného, musíme přesunout ukazatel
    # vpřed do uzlu Comment nalezeného za uzlem 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

    # Uchovávejte záznam původních uzlů předaných této metodě, abyste v případě potřeby rozdělili uzly značek.
    originalStartNode = startNode
    originalEndNode = endNode

    # Extrahujte obsah na základě uzlů na úrovni bloku (odstavců a tabulek). Procházejte nadřazené uzly a najděte je.
    # Rozdělíme obsah prvního a posledního uzlu v závislosti na tom, zda jsou uzly značek vložené.
    startNode = get_ancestor_in_body(startNode)
    endNode = get_ancestor_in_body(endNode)

    isExtracting = True
    isStartingNode = True
    # Aktuální uzel, který extrahujeme z dokumentu.
    currNode = startNode

    # Začněte extrahovat obsah. Zpracujte všechny uzly na úrovni bloku a konkrétně rozdělte první
    # a poslední uzly v případě potřeby, takže formátování odstavce je zachováno.
    # Metoda je trochu složitější než běžný extraktor, protože musíme faktor
    # při extrakci pomocí vložených uzlů, polí, záložek atd., aby to bylo užitečné.
    while (isExtracting) :
        
        # Naklonujte aktuální uzel a jeho potomky, abyste získali kopii.
        cloneNode = currNode.clone(True)
        isEndingNode = currNode == endNode

        if (isStartingNode or isEndingNode) :
            
            # Potřebujeme zpracovat každou značku zvlášť, takže ji místo toho předejte samostatné metodě.
            # End by měl být nejprve zpracován, aby se zachovaly indexy uzlů.
            if (isEndingNode) :
                # !isStartingNode: nepřidávejte uzel dvakrát, pokud jsou značky stejný uzel.
                process_marker(cloneNode, nodes, originalEndNode, currNode, isInclusive, False, not isStartingNode, False)
                isExtracting = False

            # Podmíněné musí být oddělené jako značka začátku a konce úrovně bloku, možná stejný uzel.
            if (isStartingNode) :
                process_marker(cloneNode, nodes, originalStartNode, currNode, isInclusive, True, True, False)
                isStartingNode = False
            
        else :
            # Uzel není značka začátku nebo konce, jednoduše přidejte kopii do seznamu.
            nodes.append(cloneNode)

        # Přesuňte se na další uzel a extrahujte jej. Pokud je další uzel Žádný,
        # zbytek obsahu najdete v jiné sekci.
        if (currNode.next_sibling == None and isExtracting) :
            # Přejít na další sekci.
            nextSection = currNode.get_ancestor(aw.NodeType.SECTION).next_sibling.as_section()
            currNode = nextSection.body.first_child
            
        else :
            # Přesuňte se na další uzel v těle.
            currNode = currNode.next_sibling
            
    # Pro kompatibilitu s režimem s vloženými záložkami přidejte další odstavec (prázdný).
    if (isInclusive and originalEndNode == endNode and not originalEndNode.is_composite) :
        include_next_paragraph(endNode, nodes)

    # Vraťte uzly mezi značky uzlů.
    return nodes

Některé pomocné metody jsou také vyžadovány metodou extraktu obsahu k provedení operace extrakce textu, které jsou uvedeny níže.

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

    # Důležité je pořadí, ve kterém se tyto kontroly provádějí.
    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")

    # Zkontrolujte, že koncový uzel je za počátečním uzlem ve stromu DOM.
    # Nejprve zkontrolujte, zda jsou v různých sekcích, a pokud nejsou,
    # zkontrolujte jejich polohu v těle stejné sekce.
    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):

    # Otestujte, zda je uzel potomkem uzlu Odstavec nebo Tabulka a není odstavcem
    # nebo tabulka je možný odstavec uvnitř třídy komentářů, který je slušný jako odstavec.
    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):

    # Pokud máme co do činění s uzlem na úrovni bloku, podívejte se, zda by měl být zahrnut, a přidejte jej do seznamu.
    if node == block_level_ancestor:
        if can_add and is_inclusive:
            nodes.append(clone_node)
        return

    # cloneNode je klon blockLevelNode. Pokud uzel != blockLevelNode, blockLevelAncestor
    # je předek uzlu, což znamená, že se jedná o složený uzel.
    assert clone_node.is_composite

    # Pokud je značka uzel FieldStart, zkontrolujte, zda má být zahrnuta nebo ne.
    # Pro jednoduchost předpokládáme, že FieldStart a FieldEnd se objeví ve stejném odstavci.
    if node.node_type == aw.NodeType.FIELD_START:
        # Pokud je značka počátečním uzlem a není zahrnuta, přeskočte na konec pole.
        # Pokud je značka koncovým uzlem a má být zahrnuta, přesuňte se na koncové pole, aby pole nebylo odstraněno.
        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

    # Podpořte případ, pokud je uzel značky na třetí úrovni těla dokumentu nebo níže.
    node_branch = fill_self_and_parents(node, block_level_ancestor)

    # Zpracujte odpovídající uzel v našem klonovaném uzlu podle indexu.
    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)

    # Po zpracování se může složený uzel vyprázdnit, pokud jej nezahrnuje.
    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:

        # Přejděte k prvnímu podřízenému prvku a zahrňte odstavce bez obsahu.
        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()
    # Odeberte první odstavec z prázdného dokumentu.
    dst_doc.first_section.body.remove_all_children()

    # Importujte každý uzel ze seznamu do nového dokumentu. Zachovejte původní formátování uzlu.
    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

Nyní jsme připraveni použít tyto metody a extrahovat text z dokumentu aplikace Word.

Extrahujte text mezi odstavci v dokumentu aplikace Word

Podívejme se, jak extrahovat obsah mezi dvěma odstavci v dokumentu Word DOCX. Následují kroky k provedení této operace v Python.

  • Nejprve načtěte dokument aplikace Word pomocí třídy Document.
  • Získejte odkaz na počáteční a koncový odstavce do dvou objektů pomocí metody Document.firstsection.body.getchild(NodeType.PARAGRAPH, int, boolean).asparagraph().
  • Voláním metody extractcontent(startPara, endPara, True) extrahujte uzly do objektu.
  • Zavolejte pomocnou metodu vygenerovaného dokumentu (Document, extractNodes) k vytvoření dokumentu sestávajícího z extrahovaného obsahu.
  • Nakonec uložte vrácený dokument pomocí metody Document.save(string).

Následující ukázka kódu ukazuje, jak extrahovat text mezi 7. a 11. odstavcem v dokumentu aplikace Word v Python.

# Načíst dokument.
doc = aw.Document("Extract content.docx")

# Definujte počáteční a koncové odstavce.
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()

# Extrahujte obsah mezi těmito odstavci v dokumentu. Zahrňte tyto značky do extrakce.
extractedNodes = extract_content(startPara, endPara, True)

# Vygenerujte dokument obsahující extrahovaný obsah.
dstDoc = generate_document(doc, extractedNodes)

# Uložit dokument.
dstDoc.save("extract_content_between_paragraphs.docx")

Extrahujte text mezi různými typy uzlů v dokumentu aplikace Word

Můžete také extrahovat obsah mezi různými typy uzlů. Pro demonstraci vyjmeme obsah mezi odstavcem a tabulkou a uložíme jej do nového dokumentu aplikace Word. Následují kroky k provedení této operace.

  • Načtěte dokument aplikace Word pomocí třídy Document.
  • Získejte odkaz na počáteční a koncový uzel do dvou objektů pomocí metody Document.firstsection.body.getchild(NodeType, int, boolean).
  • Voláním metody extractcontent(startPara, endPara, True) extrahujte uzly do objektu.
  • Zavolejte pomocnou metodu vygenerovaného dokumentu (Document, extractNodes) k vytvoření dokumentu sestávajícího z extrahovaného obsahu.
  • Uložte vrácený dokument pomocí metody Document.save(string).

Následující ukázka kódu ukazuje, jak extrahovat text mezi odstavcem a tabulkou v Python.

# Načíst dokument
doc = aw.Document("Extract content.docx")

# Definujte počáteční a koncový uzel.
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()

# Extrahujte obsah mezi těmito uzly v dokumentu. Zahrňte tyto značky do extrakce.
extracted_nodes = extract_content(start_para, end_table, True)

# Vygenerujte dokument obsahující extrahovaný obsah.
dstDoc = generate_document(doc, extractedNodes)

# Uložit dokument.
dstDoc.save("extract_content_between_nodes.docx")

Extrahujte text mezi odstavci na základě stylů

Pojďme se nyní podívat, jak extrahovat obsah mezi odstavci na základě stylů. Pro demonstraci se chystáme extrahovat obsah mezi prvním „Nadpisem 1“ a prvním „Nadpisem 3“ v dokumentu Word. Následující kroky ukazují, jak toho v Python dosáhnout.

  • Nejprve načtěte dokument aplikace Word pomocí třídy Document.
  • Poté pomocí pomocné metody odstavcesbystylename(Document, “Nadpis 1”) extrahujte odstavce do objektu.
  • Extrahujte odstavce do jiného objektu pomocí pomocné metody odstavcesbystylename(Document, “Nadpis 3”).
  • Zavolejte metodu extractcontent(startPara, endPara, True) a předejte první prvky v obou polích odstavců jako první a druhý parametr.
  • Zavolejte pomocnou metodu vygenerovaného dokumentu (Document, extractNodes) k vytvoření dokumentu sestávajícího z extrahovaného obsahu.
  • Nakonec uložte vrácený dokument pomocí metody Document.save(string).

Následující ukázka kódu ukazuje, jak extrahovat obsah mezi odstavci na základě stylů.

# Načíst dokument
doc = aw.Document("Extract content.docx")

# Shromážděte seznam odstavců pomocí příslušných stylů nadpisů.
parasStyleHeading1 = paragraphs_by_style_name(doc, "Heading 1")
parasStyleHeading3 = paragraphs_by_style_name(doc, "Heading 3")

# Použijte první výskyt odstavců s těmito styly.
startPara1 = parasStyleHeading1[0]
endPara1 = parasStyleHeading3[0]

# Extrahujte obsah mezi těmito uzly v dokumentu. Tyto značky do extrakce nezahrnujte.
extractedNodes = extract_content(startPara1, endPara1, False)

# Vygenerujte dokument obsahující extrahovaný obsah.
dstDoc = generate_document(doc, extractedNodes)

# Uložit dokument.
dstDoc.save("extract_content_between_paragraphs_based_on-Styles.docx")

Přečtěte si více o extrakci textu

Další scénáře extrahování textu z dokumentů aplikace Word můžete prozkoumat pomocí tohoto článku dokumentace.

Extrahujte text z dokumentů aplikace Word zdarma

Můžete získat bezplatnou dočasnou licenci k extrahování textu z dokumentů aplikace Word bez omezení hodnocení.

Závěr

V tomto článku jste se naučili extrahovat text z dokumentů MS Word pomocí Python. Navíc jste viděli, jak programově extrahovat obsah mezi podobnými nebo různými typy uzlů v dokumentu aplikace Word. V Python si tak můžete vytvořit svůj vlastní textový extraktor MS Word. Kromě toho můžete prozkoumat další funkce Aspose.Words for Python pomocí dokumentace. V případě jakýchkoli dotazů nás neváhejte kontaktovat prostřednictvím našeho fóra.

Viz také