Extrahieren Sie Text aus MS Word Dokumenten in Java

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 Java extrahieren. Darüber hinaus werden wir behandeln, wie Inhalte zwischen bestimmten Elementen wie Absätzen, Tabellen usw. dynamisch extrahiert werden.

Java-Bibliothek zum Extrahieren von Text aus Word Dokumenten

Aspose.Words for Java 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 JAR-Datei der API herunterladen oder sie mit den folgenden Maven-Konfigurationen installieren.

<repository>
    <id>AsposeJavaAPI</id>
    <name>Aspose Java API</name>
    <url>https://repository.aspose.com/repo/</url>
</repository>
<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-words</artifactId>
    <version>22.6</version>
    <type>pom</type>
</dependency>

Textextraktion in Word DOC/DOCX in Java

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-DOC/DOCX 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-DOC in Java

In diesem Abschnitt implementieren wir einen Java-Textextraktor für Word Dokumente, 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 extractContent 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 extractContent, die den Inhalt zwischen den übergebenen Knoten extrahiert.

// Vollständige Beispiele und Datendateien finden Sie unter https://github.com/aspose-words/Aspose.Words-for-Java
public static ArrayList extractContent(Node startNode, Node endNode, boolean isInclusive) throws Exception {
    // Überprüfen Sie zunächst, ob die an diese Methode übergebenen Knoten für die Verwendung gültig sind.
    verifyParameterNodes(startNode, endNode);

    // Erstellen Sie eine Liste zum Speichern der extrahierten Knoten.
    ArrayList nodes = new ArrayList();

    // Bewahren Sie eine Aufzeichnung der ursprünglichen Knoten auf, die an diese Methode übergeben wurden, damit wir bei Bedarf Markierungsknoten teilen können.
    Node originalStartNode = startNode;
    Node originalEndNode = endNode;

    // Extrahieren Sie Inhalte basierend auf Knoten auf Blockebene (Absätze und Tabellen). Durchqueren Sie übergeordnete Knoten, um sie zu finden.
    // Wir werden den Inhalt des ersten und des letzten Knotens aufteilen, je nachdem, ob die Markierungsknoten inline sind
    while (startNode.getParentNode().getNodeType() != NodeType.BODY)
        startNode = startNode.getParentNode();

    while (endNode.getParentNode().getNodeType() != NodeType.BODY)
        endNode = endNode.getParentNode();

    boolean isExtracting = true;
    boolean isStartingNode = true;
    boolean isEndingNode;
    // Der aktuelle Knoten, den wir aus dem Dokument extrahieren.
    Node currNode = startNode;

    // Beginnen Sie mit dem Extrahieren von Inhalten. Verarbeiten Sie alle Knoten auf Blockebene und teilen Sie bei Bedarf gezielt den ersten und den letzten Knoten auf, damit die Absatzformatierung erhalten bleibt.
    // Die Methode ist etwas komplexer als ein normaler Extraktor, da wir die Extraktion mit Inline-Knoten, Feldern, Lesezeichen usw. berücksichtigen müssen, um sie wirklich nützlich zu machen.
    while (isExtracting) {
        // Klonen Sie den aktuellen Knoten und seine Kinder, um eine Kopie zu erhalten.
        /*System.out.println(currNode.getNodeType());
        if(currNode.getNodeType() == NodeType.EDITABLE_RANGE_START
                || currNode.getNodeType() == NodeType.EDITABLE_RANGE_END)
        {
            currNode = currNode.nextPreOrder(currNode.getDocument());
        }*/
        System.out.println(currNode);
        System.out.println(endNode);

        CompositeNode cloneNode = null;
        ///cloneNode = (CompositeNode) currNode.deepClone(true);

        Node inlineNode = null;
        if(currNode.isComposite())
        {
            cloneNode = (CompositeNode) currNode.deepClone(true);
        }
        else
        {
            if(currNode.getNodeType() == NodeType.BOOKMARK_END)
            {
                Paragraph paragraph = new Paragraph(currNode.getDocument());
                paragraph.getChildNodes().add(currNode.deepClone(true));
                cloneNode = (CompositeNode)paragraph.deepClone(true);
            }
        }

        isEndingNode = currNode.equals(endNode);

        if (isStartingNode || isEndingNode) {
            // Wir müssen jeden Marker separat verarbeiten, also übergeben Sie ihn stattdessen an eine separate Methode.
            if (isStartingNode) {
                processMarker(cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
                isStartingNode = false;
            }

            // Die Bedingung muss getrennt sein, da die Start und Endmarkierungen auf Blockebene möglicherweise derselbe Knoten sind.
            if (isEndingNode) {
                processMarker(cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
                isExtracting = false;
            }
        } else
            // Knoten ist keine Start oder Endmarkierung, fügen Sie einfach die Kopie zur Liste hinzu.
            nodes.add(cloneNode);

        // Gehen Sie zum nächsten Knoten und extrahieren Sie ihn. Wenn der nächste Knoten null ist, bedeutet dies, dass der Rest des Inhalts in einem anderen Abschnitt gefunden wird.
        if (currNode.getNextSibling() == null && isExtracting) {
            // Wechseln Sie zum nächsten Abschnitt.
            Section nextSection = (Section) currNode.getAncestor(NodeType.SECTION).getNextSibling();
            currNode = nextSection.getBody().getFirstChild();
        } else {
            // Wechseln Sie zum nächsten Knoten im Körper.
            currNode = currNode.getNextSibling();
        }
    }

    // Geben Sie die Knoten zwischen den Knotenmarkierungen zurück.
    return nodes;
}

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

/**
 * Überprüft, ob die Eingabeparameter korrekt sind und verwendet werden können. Löst eine Ausnahme aus
 * wenn es irgendein Problem gibt.
 */
private static void verifyParameterNodes(Node startNode, Node endNode) throws Exception {
	// Die Reihenfolge, in der diese Überprüfungen durchgeführt werden, ist wichtig.
	if (startNode == null)
		throw new IllegalArgumentException("Start node cannot be null");
	if (endNode == null)
		throw new IllegalArgumentException("End node cannot be null");

	if (!startNode.getDocument().equals(endNode.getDocument()))
		throw new IllegalArgumentException("Start node and end node must belong to the same document");

	if (startNode.getAncestor(NodeType.BODY) == null || endNode.getAncestor(NodeType.BODY) == null)
		throw new IllegalArgumentException("Start node and end node must be a child or descendant of a body");

	// Überprüfen Sie, ob der Endknoten nach dem Startknoten im DOM-Baum liegt
	// Überprüfen Sie zuerst, ob sie sich in verschiedenen Abschnitten befinden, und überprüfen Sie dann, ob dies nicht der Fall ist
	// ihre Position im Hauptteil derselben Sektion, in der sie sich befinden.
	Section startSection = (Section) startNode.getAncestor(NodeType.SECTION);
	Section endSection = (Section) endNode.getAncestor(NodeType.SECTION);

	int startIndex = startSection.getParentNode().indexOf(startSection);
	int endIndex = endSection.getParentNode().indexOf(endSection);

	if (startIndex == endIndex) {
		if (startSection.getBody().indexOf(startNode) > endSection.getBody().indexOf(endNode))
			throw new IllegalArgumentException("The end node must be after the start node in the body");
	} else if (startIndex > endIndex)
		throw new IllegalArgumentException("The section of end node must be after the section start node");
}

/**
 * Überprüft, ob ein übergebener Knoten ein Inline-Knoten ist.
 */
private static boolean isInline(Node node) throws Exception {
	// Testen Sie, ob der Knoten Nachkomme eines Absatz oder Tabellenknotens und auch kein Knoten ist
	// Absatz oder eine Tabelle ein Absatz innerhalb einer Kommentarklasse, die von dieser abstammt
	// ein Absatz ist möglich.
	return ((node.getAncestor(NodeType.PARAGRAPH) != null || node.getAncestor(NodeType.TABLE) != null)
			&& !(node.getNodeType() == NodeType.PARAGRAPH || node.getNodeType() == NodeType.TABLE));
}

/**
 * Entfernt den Inhalt vor oder nach der Markierung im geklonten Knoten, je nachdem
 * auf die Art des Markers.
 */
private static void processMarker(CompositeNode cloneNode, ArrayList nodes, Node node, boolean isInclusive,
		boolean isStartMarker, boolean isEndMarker) throws Exception {
	// Wenn wir es mit einem Knoten auf Blockebene zu tun haben, sehen Sie einfach, ob er enthalten sein sollte
	// und füge es der Liste hinzu.
	if (!isInline(node)) {
		// Fügen Sie den Knoten nicht zweimal hinzu, wenn die Markierungen derselbe Knoten sind
		if (!(isStartMarker && isEndMarker)) {
			if (isInclusive)
				nodes.add(cloneNode);
		}
		return;
	}

	// Wenn ein Marker ein FieldStart-Knoten ist, prüfen Sie, ob er eingeschlossen werden soll oder nicht.
	// Wir gehen der Einfachheit halber davon aus, dass FieldStart und FieldEnd im selben Feld erscheinen
	// Absatz.
	if (node.getNodeType() == NodeType.FIELD_START) {
		// Wenn die Markierung ein Startknoten ist und nicht eingeschlossen wird, dann springe zum Ende von
		// das Feld.
		// Wenn die Markierung ein Endknoten ist und eingeschlossen werden soll, bewegen Sie sich zum Ende
		// Feld, sodass das Feld nicht entfernt wird.
		if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive)) {
			while (node.getNextSibling() != null && node.getNodeType() != NodeType.FIELD_END)
				node = node.getNextSibling();

		}
	}

	// Wenn eine der Markierungen Teil eines Kommentars ist, müssen wir den Kommentar selbst einschließen
	// müssen Sie den Mauszeiger nach vorne zum Kommentar bewegen
	// Knoten, der nach dem CommentRangeEnd-Knoten gefunden wurde.
	if (node.getNodeType() == NodeType.COMMENT_RANGE_END) {
		while (node.getNextSibling() != null && node.getNodeType() != NodeType.COMMENT)
			node = node.getNextSibling();

	}

	// Suchen Sie den entsprechenden Knoten in unserem geklonten Knoten nach Index und geben Sie ihn zurück.
	// Wenn der Start und der Endknoten gleich sind, haben einige untergeordnete Knoten möglicherweise bereits
	// Wurde entfernt. Subtrahieren Sie die
	// Unterschied, um den richtigen Index zu erhalten.
	int indexDiff = node.getParentNode().getChildNodes().getCount() - cloneNode.getChildNodes().getCount();

	// Anzahl untergeordneter Knoten identisch.
	if (indexDiff == 0)
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node));
	else
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node) - indexDiff);

	// Entfernen Sie die Knoten bis zum/vom Marker.
	boolean isSkip;
	boolean isProcessing = true;
	boolean isRemoving = isStartMarker;
	Node nextNode = cloneNode.getFirstChild();

	while (isProcessing && nextNode != null) {
		Node currentNode = nextNode;
		isSkip = false;

		if (currentNode.equals(node)) {
			if (isStartMarker) {
				isProcessing = false;
				if (isInclusive)
					isRemoving = false;
			} else {
				isRemoving = true;
				if (isInclusive)
					isSkip = true;
			}
		}

		nextNode = nextNode.getNextSibling();
		if (isRemoving && !isSkip)
			currentNode.remove();
	}

	// Nach der Verarbeitung kann der zusammengesetzte Knoten leer werden. Wenn dies der Fall ist, schließen Sie es nicht ein
	// es.
	if (!(isStartMarker && isEndMarker)) {
		if (cloneNode.hasChildNodes())
			nodes.add(cloneNode);
	}
}

public static Document generateDocument(Document srcDoc, ArrayList nodes) throws Exception {

	// Erstellen Sie ein leeres Dokument.
	Document dstDoc = new Document();
	// Entfernen Sie den ersten Absatz aus dem leeren Dokument.
	dstDoc.getFirstSection().getBody().removeAllChildren();

	// Importieren Sie jeden Knoten aus der Liste in das neue Dokument. Behalten Sie das Original
	// Formatierung des Knotens.
	NodeImporter importer = new NodeImporter(srcDoc, dstDoc, ImportFormatMode.KEEP_SOURCE_FORMATTING);

	for (Node node : (Iterable<Node>) nodes) {
		Node importNode = importer.importNode(node, true);
		dstDoc.getFirstSection().getBody().appendChild(importNode);
	}

	// Geben Sie das generierte Dokument zurück.
	return dstDoc;
}

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

Java Text zwischen Absätzen in einem Word-DOC extrahieren

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 Java aufgeführt.

  • Laden Sie zuerst das Word Dokument mit der Document Klasse.
  • Rufen Sie mithilfe der Methode Document.getFirstSection().getChild(NodeType.PARAGRAPH, int, bool) die Referenz der Anfangs und Endabsätze in zwei Objekte ab.
  • Rufen Sie die Methode extractContent(startPara, endPara, true) auf, um die Knoten in ein Objekt zu extrahieren.
  • Rufen Sie die Hilfsmethode generateDocument(Document, extractedNodes) auf, um ein Dokument zu erstellen, das aus dem extrahierten Inhalt besteht.
  • Speichern Sie schließlich 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-DOCX in Java extrahiert wird.

// Dokument laden
Document doc = new Document("TestFile.doc");

// Sammeln Sie die Knoten. Die GetChild-Methode verwendet einen 0-basierten Index
Paragraph startPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 6, true);
Paragraph endPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 10, true);
// Extrahieren Sie den Inhalt zwischen diesen Knoten im Dokument. Fügen Sie diese hinzu
// Markierungen in der Extraktion.
ArrayList extractedNodes = extractContent(startPara, endPara, true);

// Fügen Sie den Inhalt in ein neues separates Dokument ein und speichern Sie es auf der Festplatte.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Java Text aus DOC extrahieren - Zwischen verschiedenen Knotentypen

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 Extrahieren von Text zwischen verschiedenen Knoten in einem Word Dokument in Java aufgeführt.

  • Laden Sie das Word Dokument mithilfe der Document Klasse.
  • Rufen Sie mithilfe der Methode Document.getFirstSection().getChild(NodeType, int, bool) die Referenz der Start und Endknoten in zwei Objekte ab.
  • Rufen Sie die Methode extractContent(startPara, endPara, true) auf, um die Knoten in ein Objekt zu extrahieren.
  • Rufen Sie die Hilfsmethode generateDocument(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 einem DOCX mit Java extrahiert wird.

// Dokumente laden
Document doc = new Document("TestFile.doc");

// Holen Sie sich die Referenz des Anfangsabsatzes
Paragraph startPara = (Paragraph) doc.getLastSection().getChild(NodeType.PARAGRAPH, 2, true);
Table endTable = (Table) doc.getLastSection().getChild(NodeType.TABLE, 0, true);

// Extrahieren Sie den Inhalt zwischen diesen Knoten im Dokument. Schließen Sie diese Markierungen in die Extraktion ein.
ArrayList extractedNodes = extractContent(startPara, endTable, true);

// Lassen Sie uns das Array umkehren, um das Einfügen des Inhalts wieder in das Dokument zu vereinfachen.
Collections.reverse(extractedNodes);

while (extractedNodes.size() > 0) {
    // Fügen Sie den letzten Knoten aus der umgekehrten Liste ein
    endTable.getParentNode().insertAfter((Node) extractedNodes.get(0), endTable);
    // Entfernen Sie diesen Knoten nach dem Einfügen aus der Liste.
    extractedNodes.remove(0);
}

// Speichern Sie das generierte Dokument auf der Festplatte.
doc.save("output.doc");

Java Extrahieren von Text aus DOCX - 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 Java erreichen.

  • Laden Sie zuerst das Word Dokument mit der Document Klasse.
  • Extrahieren Sie dann Absätze mithilfe der Hilfsmethode paragraphsByStyleName(Document, “Heading 1”) in ein Objekt.
  • Extrahieren Sie Absätze mithilfe der Hilfsmethode “paragraphsByStyleName(Document, “Heading 3”)” in ein anderes Objekt.
  • Rufen Sie die Methode extractContent(startPara, endPara, true) auf und übergeben Sie die ersten Elemente in beiden Absatzarrays als ersten und zweiten Parameter.
  • Rufen Sie die Hilfsmethode generateDocument(Document, extractedNodes) auf, um ein Dokument zu erstellen, das aus dem extrahierten Inhalt besteht.
  • Speichern Sie schließlich das zurückgegebene Dokument mit der Methode Document.save(String).

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

// Dokument laden
Document doc = new Document(dataDir + "TestFile.doc");

// Sammeln Sie eine Liste der Absätze mit den entsprechenden Überschriftenstilen.
ArrayList parasStyleHeading1 = paragraphsByStyleName(doc, "Heading 1");
ArrayList parasStyleHeading3 = paragraphsByStyleName(doc, "Heading 3");

// Verwenden Sie die erste Instanz der Absätze mit diesen Stilen.
Node startPara1 = (Node) parasStyleHeading1.get(0);
Node endPara1 = (Node) parasStyleHeading3.get(0);

// Extrahieren Sie den Inhalt zwischen diesen Knoten im Dokument. Schließen Sie diese Markierungen nicht in die Extraktion ein.
ArrayList extractedNodes = extractContent(startPara1, endPara1, false);

// Fügen Sie den Inhalt in ein neues separates Dokument ein und speichern Sie es auf der Festplatte.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Java Word Text Extractor - Mehr erfahren

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

Java-API zum Extrahieren von Text aus DOC/DOCX - Holen Sie sich eine kostenlose Lizenz

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

Fazit

In diesem Artikel haben Sie gelernt, wie Sie Text aus MS Word DOC DOCX in Java 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 Java erstellen. Außerdem können Sie andere Funktionen von Aspose.Words for Java mithilfe der Dokumentation erkunden. Falls Sie Fragen haben, können Sie uns diese gerne über unser Forum mitteilen.

Siehe auch