استخراج محتوا از اسناد Word DOCX در پایتون

استخراج متن از اسناد Word اغلب در سناریوهای مختلف انجام می شود. به عنوان مثال، برای تجزیه و تحلیل متن، استخراج بخش های خاصی از یک سند و ترکیب آنها در یک سند واحد و غیره. در این مقاله نحوه استخراج متن از اسناد ورد به صورت برنامه نویسی در پایتون را خواهید آموخت. علاوه بر این، نحوه استخراج محتوا بین عناصر خاص مانند پاراگراف ها، جداول و غیره را به صورت پویا توضیح خواهیم داد.

کتابخانه پایتون برای استخراج متن از اسناد Word

Aspose.Words for Python یک کتابخانه قدرتمند است که به شما امکان می دهد اسناد MS Word را از ابتدا ایجاد کنید. علاوه بر این، به شما امکان می دهد اسناد Word موجود را برای رمزگذاری، تبدیل، استخراج متن و غیره دستکاری کنید. ما از این کتابخانه برای استخراج متن از اسناد Word DOCX یا DOC استفاده خواهیم کرد. با استفاده از دستور pip زیر می توانید کتابخانه را از PyPI نصب کنید.

pip install aspose-words

استخراج متن در اسناد Word با استفاده از پایتون

یک سند MS Word از عناصر مختلفی تشکیل شده است که شامل پاراگراف ها، جداول، تصاویر و غیره می شود. بنابراین، الزامات استخراج متن می تواند از سناریویی به سناریوی دیگر متفاوت باشد. به عنوان مثال، ممکن است لازم باشد متنی را بین پاراگراف ها، نشانک ها، نظرات و غیره استخراج کنید.

هر نوع عنصر در یک سند Word به عنوان یک گره نشان داده می شود. بنابراین، برای پردازش یک سند، باید با گره ها بازی کنید. پس بیایید شروع کنیم و نحوه استخراج متن از اسناد Word را در سناریوهای مختلف ببینیم.

متن را از یک سند Word در پایتون استخراج کنید

در این بخش قصد داریم یک استخراج کننده متن پایتون برای اسناد Word پیاده سازی کنیم و گردش کار استخراج متن به صورت زیر خواهد بود:

  • ابتدا گره هایی را که می خواهیم در فرآیند استخراج متن قرار دهیم را تعریف می کنیم.
  • سپس، محتوا را بین گره های مشخص شده (شامل یا حذف گره های شروع و پایان) استخراج می کنیم.
  • در نهایت، ما از کلون گره های استخراج شده استفاده می کنیم، به عنوان مثال برای ایجاد یک سند Word جدید متشکل از محتوای استخراج شده.

اکنون متدی به نام extractcontent می نویسیم که گره ها و برخی پارامترهای دیگر را برای انجام استخراج متن به آن پاس می دهیم. این روش سند را تجزیه و گره ها را شبیه سازی می کند. در زیر پارامترهایی هستند که به این متد ارسال می کنیم.

  1. StartNode و EndNode به ترتیب به عنوان نقطه شروع و پایان برای استخراج محتوا. این گره‌ها می‌توانند هر دو سطح بلوک (پاراگراف، جدول) یا سطح درون خطی (به عنوان مثال Run، FieldStart، BookmarkStart و غیره) باشند.
    1. برای ارسال یک فیلد باید شی FieldStart مربوطه را ارسال کنید.
    2. برای ارسال نشانک ها، گره های BookmarkStart و BookmarkEnd باید ارسال شوند.
    3. برای نظرات، گره های CommentRangeStart و CommentRangeEnd باید استفاده شوند.
  2. IsInclusive مشخص می کند که آیا نشانگرها در استخراج گنجانده شده اند یا خیر. اگر این گزینه روی false تنظیم شود و همان گره یا گره های متوالی ارسال شود، یک لیست خالی برگردانده می شود.

در ادامه پیاده‌سازی کامل روش استخراج محتوایی است که محتوای بین گره‌های ارسال شده را استخراج می‌کند.

def extract_content(startNode : aw.Node, endNode : aw.Node, isInclusive : bool):
    
    # ابتدا بررسی کنید که گره های ارسال شده به این روش برای استفاده معتبر هستند.
    verify_parameter_nodes(startNode, endNode)

    # یک لیست برای ذخیره گره های استخراج شده ایجاد کنید.
    nodes = []

    # اگر هر یک از نشانگرها بخشی از یک نظر است، از جمله خود نظر، باید نشانگر را جابجا کنیم
    # به سمت گره نظری که بعد از گره 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) :
            
            # ما باید هر نشانگر را جداگانه پردازش کنیم، بنابراین در عوض آن را به یک روش جداگانه منتقل کنید.
            # End باید ابتدا پردازش شود تا شاخص های گره حفظ شود.
            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 :
            # Node یک نشانگر شروع یا پایان نیست، به سادگی کپی را به لیست اضافه کنید.
            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

برخی از روش‌های کمکی نیز با روش استخراج محتوا برای انجام عملیات استخراج متن مورد نیاز هستند که در زیر آورده شده‌اند.

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 است. اگر گره != 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 را ببینیم. در ادامه مراحل انجام این عملیات در پایتون آمده است.

  • ابتدا سند Word را با استفاده از کلاس Document بارگذاری کنید.
  • با استفاده از روش Document.firstsection.body.getchild(NodeType.PARAGRAPH, int, boolean).asparagraph، مرجع پاراگراف های شروع و پایان را به دو شیء دریافت کنید.
  • برای استخراج گره ها در یک شی، روش extractcontent (startPara، endPara، True) را فراخوانی کنید.
  • برای ایجاد سند متشکل از محتوای استخراج‌شده، روش کمکی تولید سند (سند، استخراج‌شده نود) را فراخوانی کنید.
  • در نهایت، سند برگشتی را با استفاده از روش Document.save(string) ذخیره کنید.

نمونه کد زیر نحوه استخراج متن بین پاراگراف های 7 و 11 را در یک سند Word در پایتون نشان می دهد.

# بارگذاری سند
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) را فراخوانی کنید.
  • برای ایجاد سند متشکل از محتوای استخراج‌شده، روش کمکی تولید سند (سند، استخراج‌شده نود) را فراخوانی کنید.
  • سند برگشتی را با استفاده از روش Document.save(string) ذخیره کنید.

نمونه کد زیر نحوه استخراج متن بین پاراگراف و جدول در پایتون را نشان می دهد.

# بارگذاری سند
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 را با استفاده از کلاس Document بارگذاری کنید.
  • سپس، پاراگراف ها را با استفاده از روش کمکی paragraphsbystylename (Document, “Heading 1”) در یک شی استخراج کنید.
  • با استفاده از روش کمکی paragraphsbystylename(Document, “Heading 3”) پاراگراف ها را در یک شی دیگر استخراج کنید.
  • متد extractcontent (startPara, endPara, True) را فراخوانی کنید و عناصر اول را در هر دو آرایه پاراگراف به عنوان پارامترهای اول و دوم ارسال کنید.
  • برای ایجاد سند متشکل از محتوای استخراج‌شده، روش کمکی تولید سند (سند، استخراج‌شده نود) را فراخوانی کنید.
  • در نهایت، سند برگشتی را با استفاده از روش 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 را با استفاده از [این 4 مقاله مستند بررسی کنید.

مجوز API رایگان دریافت کنید

می توانید برای استفاده از Aspose.Words برای پایتون بدون محدودیت ارزیابی، مجوز موقت دریافت کنید.

نتیجه

در این مقاله نحوه استخراج متن از اسناد MS Word با استفاده از پایتون را آموختید. علاوه بر این، نحوه استخراج محتوا بین انواع مشابه یا متفاوت گره ها در یک سند Word را به صورت برنامه نویسی دیده اید. بنابراین، می توانید استخراج کننده متن MS Word خود را در پایتون بسازید. علاوه بر این، می‌توانید سایر ویژگی‌های Aspose.Words برای پایتون را با استفاده از مستندات کاوش کنید. در صورت داشتن هرگونه سوال، از طریق [تالار گفتمان7 ما را در جریان بگذارید.

همچنین ببینید

اطلاعات: اگر زمانی نیاز به دریافت یک سند Word از یک ارائه پاورپوینت دارید، می توانید از مبدل Aspose Presentation to Word Document استفاده کنید.