在Python中将docx按标题拆分为单独的文件

在Python中将docx按标题拆分为单独的文件,python,xml,docx,python-docx,Python,Xml,Docx,Python Docx,我想写一个程序,获取我的docx文件,遍历它们,并根据标题将每个文件拆分为多个单独的文件。在每个docx中都有几篇文章,每个文章下面都有“标题1”和文本 因此,如果我的原始file1.docx有4篇文章,我希望将其拆分为4个单独的文件,每个文件都有标题和文本 我找到了一个部分,它在我保存.docx文件的路径中遍历所有文件,我可以分别读取标题和文本,但我似乎无法找到一种方法来合并所有文件并将其拆分为单独的文件,每个文件都有标题和文本。我正在使用pythondocx库 import glob fro

我想写一个程序,获取我的docx文件,遍历它们,并根据标题将每个文件拆分为多个单独的文件。在每个docx中都有几篇文章,每个文章下面都有“标题1”和文本

因此,如果我的原始file1.docx有4篇文章,我希望将其拆分为4个单独的文件,每个文件都有标题和文本

我找到了一个部分,它在我保存.docx文件的路径中遍历所有文件,我可以分别读取标题和文本,但我似乎无法找到一种方法来合并所有文件并将其拆分为单独的文件,每个文件都有标题和文本。我正在使用pythondocx库

import glob
from docx import Document

headings = []
texts = []

def iter_headings(paragraphs):
    for paragraph in paragraphs:
        if paragraph.style.name.startswith('Heading'):
            yield paragraph

def iter_text(paragraphs):
    for paragraph in paragraphs:
        if paragraph.style.name.startswith('Normal'):
            yield paragraph

for name in glob.glob('/*.docx'):
    document = Document(name)
    for heading in iter_headings(document.paragraphs):
        headings.append(heading.text)
        for paragraph in iter_text(document.paragraphs):
            texts.append(paragraph.text)
    print(texts)
如何提取每篇文章的文本和标题

这是PythonDocx给我的XML阅读工具。红色大括号标记我要从每个文件中提取的内容


对于如何用不同的方法实现我想要的目标,或者是否有更简单的方法使用PDF文件,我愿意接受任何其他建议

我认为使用迭代器的方法是合理的,但我倾向于以不同的方式对它们进行打包。在最高层,您可以:

for paragraphs in iterate_document_sections(document.paragraphs):
    create_document_from_paragraphs(paragraphs)
然后,
iterate\u document\u sections()
看起来像:

def iterate_document_sections(document):
    """Generate a sequence of paragraphs for each headed section in document.

    Each generated sequence has a heading paragraph in its first position, 
    followed by one or more body paragraphs.
    """
    paragraphs = [document.paragraphs[0]]
    for paragraph in document.paragraphs[1:]:
        if is_heading(paragraph):
             yield paragraphs
             paragraphs = [paragraph]
             continue
        paragraphs.append(paragraph)
    yield paragraphs
类似这样的东西,再加上其他代码的部分,应该可以让您从一开始就可以使用。您需要实现
is\u heading()
create\u document\u from\u parations()


请注意,这里的术语“节”在常用的出版术语中用于指代(节)标题及其附属段落,而不是指Word文档节对象(如
document.sections
)。

事实上,只有当文档除段落外没有任何其他元素时,提供的解决方案才能很好地工作(例如,表格)

另一种可能的解决方案是,不仅要迭代段落,还要迭代文档体的所有子xml元素。一旦找到“子文档”的开始和结束元素(示例中带有标题的段落),就应该删除与此部分无关的其他xml元素(一种切断所有其他文档内容的方式)。这样,您可以保留所有样式、文本、表格和其他文档元素及格式。 这不是一个优雅的解决方案,意味着您必须在内存中保留完整源文档的临时副本

这是我的代码:

import tempfile
from typing import Generator, Tuple, Union

from docx import Document
from docx.document import Document as DocType
from docx.oxml.table import CT_Tbl
from docx.oxml.text.paragraph import CT_P
from docx.oxml.xmlchemy import BaseOxmlElement
from docx.text.paragraph import Paragraph


def iterparts(doc_path:str, skip_first=True, bias:int=0) -> Generator[Tuple[int,DocType],None,None]:
    """Iterate over sub-documents by splitting source document into parts
    Split into parts by copying source document and cutting off unrelevant
    data.

    Args:
        doc_path (str):                 path to source *docx* file
        skip_first (bool, optional):    skip first split point and wait for 
                                        second occurrence. Defaults to True.
        bias (int, optional):           split point bias. Defaults to 0.

    Yields:
        Generator[Tuple[int,DocType],None,None]:    first element of each tuple 
                                                    indicates the number of a 
                                                    sub-document, if number is 0 
                                                    then there are no sub-documents
    """
    doc = Document(doc_path)
    counter = 0
    while doc:
        split_elem_idx = -1
        doc_body = doc.element.body
        cutted = [doc, None]
        for idx, elem in enumerate(doc_body.iterchildren()):
            if is_split_point(elem):
                if split_elem_idx == -1 and skip_first:
                    split_elem_idx = idx
                else:
                    cutted = split(doc, idx+bias) # idx-1 to keep previous paragraph
                    counter += 1
                    break
        yield (counter, cutted[0])
        doc = cutted[1]

def is_split_point(element:BaseOxmlElement) -> bool:
    """Split criteria

    Args:
        element (BaseOxmlElement): oxml element

    Returns:
        bool: whether the *element* is the beginning of a new sub-document
    """
    if isinstance(element, CT_P):
        p = Paragraph(element, element.getparent())
        return p.text.startswith("Some text")
    return False

def split(doc:DocType, cut_idx:int) -> Tuple[DocType,DocType]:
    """Splitting into parts by copying source document and cutting of
    unrelevant data.

    Args:
        doc (DocType): [description]
        cut_idx (int): [description]

    Returns:
        Tuple[DocType,DocType]: [description]
    """
    tmpdocfile = write_tmp_doc(doc)
    second_part = doc
    second_elems = list(second_part.element.body.iterchildren())
    for i in range(0, cut_idx):
        remove_element(second_elems[i])
    first_part = Document(tmpdocfile)
    first_elems = list(first_part.element.body.iterchildren())
    for i in range(cut_idx, len(first_elems)):
        remove_element(first_elems[i])
    tmpdocfile.close()
    return (first_part, second_part)

def remove_element(elem: Union[CT_P,CT_Tbl]):
    elem.getparent().remove(elem)

def write_tmp_doc(doc:DocType):
    tmp = tempfile.TemporaryFile()
    doc.save(tmp)
    return tmp

请注意,您应该根据您的拆分标准定义
is\u split\u point
方法

寻求调试帮助的问题(“为什么此代码不起作用?”)必须包括所需的行为、特定的问题或错误以及在问题本身中重现这些问题所需的最短代码。没有明确问题说明的问题对其他读者来说是没有用处的。请参阅:并包括您迄今为止获得的代码。好的,我添加了代码,希望我之前的意思更清楚。抱歉,这是我的问题rst邮政。