חלץ תוכן ממסמכי Word DOCX ב- Python

חילוץ טקסט ממסמכי Word מבוצע לרוב בתרחישים שונים. לדוגמה, לנתח את הטקסט, לחלץ חלקים מסוימים של מסמך ולשלב אותם למסמך בודד, וכן הלאה. במאמר זה תלמדו כיצד לחלץ טקסט ממסמכי Word באופן תכנותי ב-Python. יתרה מכך, נסקור כיצד לחלץ תוכן בין אלמנטים ספציפיים כגון פסקאות, טבלאות וכו’ באופן דינמי.

ספריית Python לחילוץ טקסט ממסמכי Word

Aspose.Words for Python היא ספרייה רבת עוצמה המאפשרת לך ליצור מסמכי MS Word מאפס. יתר על כן, זה מאפשר לך לתפעל מסמכי Word קיימים לצורך הצפנה, המרה, חילוץ טקסט וכו’. אנו נשתמש בספרייה זו כדי לחלץ טקסט ממסמכי Word DOCX או DOC. אתה יכול להתקין את הספרייה מ-PyPI באמצעות פקודת ה-pip הבאה.

pip install aspose-words

חילוץ טקסט במסמכי Word באמצעות Python

מסמך MS Word מורכב מאלמנטים שונים הכוללים פסקאות, טבלאות, תמונות וכו’. לכן, הדרישות של חילוץ טקסט עשויות להשתנות מתרחיש אחד למשנהו. לדוגמה, ייתכן שיהיה עליך לחלץ טקסט בין פסקאות, סימניות, הערות וכו'.

כל סוג של רכיב במסמך Word מיוצג כצומת. לכן, כדי לעבד מסמך, תצטרך לשחק עם הצמתים. אז בואו נתחיל ונראה כיצד לחלץ טקסט ממסמכי Word בתרחישים שונים.

חלץ טקסט ממסמך Word ב- Python

בסעיף זה, אנו הולכים ליישם מחלץ טקסט Python עבור מסמכי Word וזרימת העבודה של חילוץ הטקסט תהיה כדלקמן:

  • ראשית, נגדיר את הצמתים שברצוננו לכלול בתהליך חילוץ הטקסט.
  • לאחר מכן, נחלץ את התוכן בין הצמתים שצוינו (כולל או לא כולל את צמתי ההתחלה והסיום).
  • לבסוף, נשתמש בשיבוט של צמתים שחולצו, למשל כדי ליצור מסמך Word חדש המורכב מתוכן שחולץ.

כעת נכתוב שיטה בשם extractcontent אליה נעביר את הצמתים ועוד כמה פרמטרים לביצוע חילוץ הטקסט. שיטה זו תנתח את המסמך ותשכפל את הצמתים. להלן הפרמטרים שנעביר לשיטה זו.

  1. StartNode ו-EndNode כנקודות התחלה וסיום לחילוץ התוכן, בהתאמה. אלה יכולים להיות צמתים ברמת בלוק (סעיף, טבלה) או ברמת מוטבעת (למשל הפעלה, FieldStart, BookmarkStart וכו’).
    1. כדי להעביר שדה עליך לעבור את אובייקט FieldStart המתאים.
    2. כדי להעביר סימניות, יש לעבור את הצמתים BookmarkStart ו-BookmarkEnd.
    3. עבור הערות, יש להשתמש בצמתים CommentRangeStart ו-CommentRangeEnd.
  2. IsInclusive מגדיר אם הסמנים כלולים בחילוץ או לא. אם אפשרות זו מוגדרת כ-false ואותו צומת או צמתים עוקבים עוברים, תוחזר רשימה ריקה.

להלן היישום המלא של שיטת extractcontent המחלצת את התוכן בין הצמתים המועברים.

def extract_content(startNode : aw.Node, endNode : aw.Node, isInclusive : bool):
    
    # ראשית, בדוק שהצמתים המועברים לשיטה זו תקפים לשימוש.
    verify_parameter_nodes(startNode, endNode)

    # צור רשימה לאחסון הצמתים שחולצו.
    nodes = []

    # אם אחד מהסמנים הוא חלק מהערה, כולל ההערה עצמה, עלינו להזיז את המצביע
    # העבר לצומת Comment שנמצא אחרי הצומת 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

    # שמור תיעוד של הצמתים המקוריים שהועברו לשיטה זו כדי לפצל צמתי סמן במידת הצורך.
    originalStartNode = startNode
    originalEndNode = endNode

    # חלץ תוכן על בסיס צמתים ברמת הבלוק (פסקאות וטבלאות). חצו דרך צמתי אב כדי למצוא אותם.
    # נחלק את התוכן של הצמתים הראשון והאחרון, תלוי אם צמתי הסמן נמצאים בשורה.
    startNode = get_ancestor_in_body(startNode)
    endNode = get_ancestor_in_body(endNode)

    isExtracting = True
    isStartingNode = True
    # הצומת הנוכחי שאנו מחלצים מהמסמך.
    currNode = startNode

    # התחל לחלץ תוכן. עבד את כל הצמתים ברמת הבלוק ופצל במיוחד את הראשון
    # וצמתים אחרונים בעת הצורך, כך שעיצוב הפסקה נשמר.
    # השיטה היא קצת יותר מסובכת ממחלץ רגיל כפי שאנו צריכים להעריך
    # בחילוץ באמצעות צמתים מוטבעים, שדות, סימניות וכו' כדי להפוך אותו לשימושי.
    while (isExtracting) :
        
        # שכפל את הצומת הנוכחי והילדים שלו כדי להשיג עותק.
        cloneNode = currNode.clone(True)
        isEndingNode = currNode == endNode

        if (isStartingNode or isEndingNode) :
            
            # אנחנו צריכים לעבד כל סמן בנפרד, אז העבירו אותו לשיטה נפרדת במקום זאת.
            # יש לעבד את הסוף בהתחלה כדי לשמור על אינדקסים של צומת.
            if (isEndingNode) :
                # !isStartingNode: אל תוסיף את הצומת פעמיים אם הסמנים הם אותו צומת.
                process_marker(cloneNode, nodes, originalEndNode, currNode, isInclusive, False, not isStartingNode, False)
                isExtracting = False

            # מותנה צריך להיות נפרד כמו סמני ההתחלה והסיום של רמת הבלוק, אולי אותו צומת.
            if (isStartingNode) :
                process_marker(cloneNode, nodes, originalStartNode, currNode, isInclusive, True, True, False)
                isStartingNode = False
            
        else :
            # צומת אינו סמן התחלה או סיום, פשוט הוסף את העותק לרשימה.
            nodes.append(cloneNode)

        # עבור לצומת הבא וחלץ אותו. אם הצומת הבא הוא ללא,
        # שאר התוכן נמצא בחלק אחר.
        if (currNode.next_sibling == None and isExtracting) :
            # עבור לסעיף הבא.
            nextSection = currNode.get_ancestor(aw.NodeType.SECTION).next_sibling.as_section()
            currNode = nextSection.body.first_child
            
        else :
            # עבור לצומת הבא בגוף.
            currNode = currNode.next_sibling
            
    # לקבלת תאימות למצב עם סימניות מוטבעות, הוסף את הפסקה הבאה (ריקה).
    if (isInclusive and originalEndNode == endNode and not originalEndNode.is_composite) :
        include_next_paragraph(endNode, nodes)

    # החזר את הצמתים בין סמני הצמתים.
    return nodes

כמה שיטות עוזר נדרשות גם על ידי שיטת extractcontent כדי לבצע את פעולת חילוץ הטקסט, המפורטות להלן.

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

    # יש חשיבות לסדר ביצוע הבדיקות הללו.
    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")

    # בדוק שצומת הסיום נמצא אחרי צומת ההתחלה בעץ ה-DOM.
    # ראשית, בדוק אם הם נמצאים בקטעים שונים, ואז אם הם לא,
    # לבדוק את מיקומם בגוף של אותו קטע.
    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):

    # בדוק אם הצומת הוא צאצא של צומת פסקה או טבלה ואינו פסקה
    # או טבלה אפשרית פסקה בתוך מחלקה הערה שהיא הגונה של פסקה.
    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 node == block_level_ancestor:
        if can_add and is_inclusive:
            nodes.append(clone_node)
        return

    # cloneNode הוא שיבוט של blockLevelNode. אם node != blockLevelNode, blockLevelAncestor
    # הוא האב הקדמון של הצומת כלומר הוא צומת מורכב.
    assert clone_node.is_composite

    # אם סמן הוא צומת FieldStart, בדוק אם יש לכלול אותו או לא.
    # אנו מניחים לשם הפשטות שה-FieldStart ו-FieldEnd מופיעים באותה פסקה.
    if node.node_type == aw.NodeType.FIELD_START:
        # אם הסמן הוא צומת התחלה ואינו כלול, דלג לסוף השדה.
        # אם הסמן הוא צומת קצה ויש לכלול אותו, עברו לשדה הסיום כדי שהשדה לא יוסר.
        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

    # תמכו במארז אם צומת הסמן נמצא ברמה השלישית של גוף המסמך או נמוך יותר.
    node_branch = fill_self_and_parents(node, block_level_ancestor)

    # עבד את הצומת המתאים בצומת המשובט שלנו לפי אינדקס.
    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)

    # לאחר העיבוד, הצומת המרוכב עלול להתרוקן אם הוא לא כולל אותו.
    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:

        # עבור לילד הראשון כדי לכלול פסקאות ללא תוכן.
        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()
    # הסר את הפסקה הראשונה מהמסמך הריק.
    dst_doc.first_section.body.remove_all_children()

    # ייבא כל צומת מהרשימה למסמך החדש. שמור על העיצוב המקורי של הצומת.
    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

כעת אנו מוכנים להשתמש בשיטות אלה ולחלץ טקסט ממסמך Word.

חלץ טקסט בין פסקאות במסמך Word

בואו נראה כיצד לחלץ תוכן בין שתי פסקאות במסמך Word DOCX. להלן השלבים לביצוע פעולה זו ב- Python.

  • ראשית, טען את מסמך Word באמצעות מחלקה Document.
  • קבל הפניה של פסקאות ההתחלה והסיום לשני אובייקטים באמצעות שיטת Document.firstsection.body.getchild(NodeType.PARAGRAPH, int, boolean).asparagraph().
  • קרא לשיטת extractcontent(startPara, endPara, True) כדי לחלץ את הצמתים לאובייקט.
  • קרא לשיטת העזר generdocument(Document, extractedNodes) כדי ליצור מסמך המורכב מהתוכן שחולץ.
  • לבסוף, שמור את המסמך שהוחזר באמצעות שיטת Document.save(string).

דוגמת הקוד הבאה מראה כיצד לחלץ טקסט בין הפסקה השביעית ל-11 במסמך Word ב- Python.

# טען מסמך.
doc = aw.Document("Extract content.docx")

# הגדר פסקאות התחלה וסיום.
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()

# חלץ את התוכן בין הפסקאות הללו במסמך. כלול סמנים אלה בחילוץ.
extractedNodes = extract_content(startPara, endPara, True)

# צור מסמך המכיל תוכן שחולץ.
dstDoc = generate_document(doc, extractedNodes)

# שמור מסמך.
dstDoc.save("extract_content_between_paragraphs.docx")

חלץ טקסט בין סוגים שונים של צמתים במסמך Word

אתה יכול גם לחלץ תוכן בין סוגים שונים של צמתים. להדגמה, בואו נחלץ תוכן בין פסקה לטבלה ונשמור אותו במסמך Word חדש. להלן השלבים לביצוע פעולה זו.

  • טען את מסמך Word באמצעות מחלקה Document.
  • קבל הפניה של צמתי ההתחלה והסיום לשני אובייקטים באמצעות שיטת Document.firstsection.body.getchild(NodeType, int, boolean).
  • קרא לשיטת extractcontent(startPara, endPara, True) כדי לחלץ את הצמתים לאובייקט.
  • קרא לשיטת העזר generdocument(Document, extractedNodes) כדי ליצור מסמך המורכב מהתוכן שחולץ.
  • שמור את המסמך שהוחזר באמצעות שיטת Document.save(string).

דוגמת הקוד הבאה מראה כיצד לחלץ טקסט בין פסקה לטבלה ב-Python.

# טען מסמך
doc = aw.Document("Extract content.docx")

# הגדר צמתים התחלה וסיום.
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()

# חלץ את התוכן בין הצמתים הללו במסמך. כלול סמנים אלה בחילוץ.
extracted_nodes = extract_content(start_para, end_table, True)

# צור מסמך המכיל תוכן שחולץ.
dstDoc = generate_document(doc, extractedNodes)

# שמור מסמך.
dstDoc.save("extract_content_between_nodes.docx")

חלץ טקסט בין פסקאות בהתבסס על סגנונות

כעת נבדוק כיצד לחלץ תוכן בין פסקאות על סמך סגנונות. לצורך הדגמה, אנו הולכים לחלץ תוכן בין “כותרת 1” הראשונה ל"כותרת 3" הראשונה במסמך Word. השלבים הבאים מדגימים כיצד להשיג זאת ב- Python.

  • ראשית, טען את מסמך Word באמצעות מחלקה Document.
  • לאחר מכן, חלץ פסקאות לתוך אובייקט באמצעות שיטת העזרה paragraphsbystylename(Document, “Heading 1”).
  • חלץ פסקאות לאובייקט אחר באמצעות שיטת העזרה paragraphsbystylename(Document, “Heading 3”).
  • קראו לשיטת extractcontent(startPara, endPara, True) והעבירו את האלמנטים הראשונים בשני מערכי הפסקה כפרמטר ראשון ושני.
  • קרא לשיטת העזר generdocument(Document, extractedNodes) כדי ליצור מסמך המורכב מהתוכן שחולץ.
  • לבסוף, שמור את המסמך שהוחזר באמצעות שיטת Document.save(string).

דוגמת הקוד הבאה מראה כיצד לחלץ תוכן בין פסקאות בהתבסס על סגנונות.

# טען מסמך
doc = aw.Document("Extract content.docx")

# אסוף רשימה של הפסקאות תוך שימוש בסגנונות הכותרות המתאימים.
parasStyleHeading1 = paragraphs_by_style_name(doc, "Heading 1")
parasStyleHeading3 = paragraphs_by_style_name(doc, "Heading 3")

# השתמש במופע הראשון של הפסקאות עם הסגנונות האלה.
startPara1 = parasStyleHeading1[0]
endPara1 = parasStyleHeading3[0]

# חלץ את התוכן בין הצמתים הללו במסמך. אל תכלול את הסמנים האלה בחילוץ.
extractedNodes = extract_content(startPara1, endPara1, False)

# צור מסמך המכיל תוכן שחולץ.
dstDoc = generate_document(doc, extractedNodes)

# שמור מסמך.
dstDoc.save("extract_content_between_paragraphs_based_on-Styles.docx")

קרא עוד על חילוץ טקסט

אתה יכול לחקור תרחישים אחרים של חילוץ טקסט ממסמכי Word באמצעות מאמר התיעוד זה.

חלץ טקסט ממסמכי Word בחינם

אתה יכול לקבל [רישיון זמני] בחינם5 לחילוץ טקסט ממסמכי Word ללא מגבלות הערכה.

סיכום

במאמר זה, למדת כיצד לחלץ טקסט ממסמכי MS Word באמצעות Python. יתר על כן, ראית כיצד לחלץ תוכן בין סוגים דומים או שונים של צמתים במסמך Word באופן תכנותי. לפיכך, אתה יכול לבנות מחלץ טקסט MS Word משלך ב- Python. חוץ מזה, אתה יכול לחקור תכונות אחרות של Aspose.Words עבור Python באמצעות תיעוד. אם יהיו לך שאלות כלשהן, אל תהסס ליידע אותנו דרך הפורום שלנו.

ראה גם