Python:通过解析word / document.xml将文本从docx提取到txt

时间:2016-01-14 08:22:52

标签: python xml parsing xml-parsing docx

我想将docx文件中的文本提取到简单的txt文件中。 我知道这个问题可能看似简单或微不足道(我希望会是这样)但是我查看了几十个论坛主题,花了好几个小时试图自己解决并找不到解决方案......

我从Etienne's blog借用了以下代码。

如果我需要没有格式化的内容,它可以很好地工作。但... 由于我的文档包含简单的表格,因此我需要它们使用制表符来保持其格式。 所以不要这样:

Name
Age
Wage
John
30
2000

这应该出现:

Name      Age     Wage
John      30      2000

为了不相互滑动,我更喜欢使用双标签来获得更长的线条。 我稍微检查了一下XML结构,发现表中的新行用tr表示,列用tc表示。 所以我试图改变这一千种方法,但没有成功...... 虽然它没有真正起作用,但我复制了我接近解决方案的想法:

from lxml.html.defs import form_tags

try:
    from xml.etree.cElementTree import XML
except ImportError:
    from xml.etree.ElementTree import XML
import zipfile

WORD_NAMESPACE='{http://schemas.openxmlformats.org/wordprocessingml/2006/main}'
PARA = WORD_NAMESPACE + 'p'
TEXT = WORD_NAMESPACE + 't'
ROW = WORD_NAMESPACE + 'tr'
COL = WORD_NAMESPACE + 'tc'


def get_docx_text(path):
document = zipfile.ZipFile(path)    
xml_content = document.read('word/document.xml')
document.close()    
tree = XML(xml_content)    
paragraphs = []    

for item in tree.iter(ROW or COL or PARA):    
    texts = []
    print(item)    
    if item is ROW:    
        texts.append('\n')    
    elif item is COL:    
        texts.append('\t\t')    
    elif item is PARA:    
        for node in item.iter(TEXT):    
            if node.text:    
                texts.append(node.text)    
    if texts:    
        paragraphs.append(''.join(texts))    
return '\n\n'.join(paragraphs)

text_file = open("output.txt", "w")
text_file.write(get_docx_text('input.docx'))
text_file.close()

我不太清楚语法应该是什么样的。输出没有任何结果,并且在一些试验中它产生了一些东西,但它甚至比什么都没有。

我将print(item)仅用于检查。但不是每一个ROW,COL和PARA项目都会列出我的ROWs。因此,似乎在for循环的条件下,程序似乎进入了术语的连接或连接。如果找不到ROW,它将不会执行剩下的2个选项,而是立即跳到下一个项目。我试着给出一个条款清单。

在其中if if / elif块我认为例如if item is ROW应该检查'item'和'ROW'是否相同(实际上是它们)。

2 个答案:

答案 0 :(得分:0)

上面的答案不会像你问的那样奏效。这适用于仅包含表格的文件;使用findall进行一些额外的解析可以帮助您隔离非表数据,并使其适用于包含表和其他文本的文档:

TABLE = WORD_NAMESPACE + 'tbl'  

for item in tree.iter():   # use this for loop instead
    #print(item.tag)
    if item.tag == TABLE:
        for row in item.iter(ROW):
            texts.append('\n')
            for col in row.iter(COL):
                texts.append('\t')
                for ent in col.iter(TEXT):
                    if ent.text:
                        texts.append(ent.text)
return ''.join(texts)

答案 1 :(得分:0)

  1. X or Y or Z计算三个值中的第一个值,该值将转换为True。非空字符串始终为True。因此,for item in tree.iter(ROW or COL or PARA)的计算结果为for item in tree.iter(ROW) - 这就是为什么在循环中只获得行元素的原因。
  2. iter() ElementTree对象的方法只能接受一个标记名称,所以你应该只迭代整个树(如果文档不大则不会有问题)。
  3. is不会在这里工作。它是一个身份运算符,只有在比较的对象相同时才返回True(即变量比较指的是相同的 Python对象)。在你的if... elif...中你比较了一个常量str(ROW,COL,PARA)和Element对象,它在每次迭代中重新创建,所以,显然,这两个对象不是同一个对象比较将返回False
  4. 相反,您应该使用if item.tag == ROW
  5. 之类的内容

    考虑到以上所有因素,你应该像这样重写你的循环部分:

    for item in tree.iter():    
        texts = []
        print(item)    
        if item.tag == ROW:    
            texts.append('\n')    
        elif item.tag == COL:    
            texts.append('\t\t')    
        elif item.tag == PARA:    
            for node in item.iter(TEXT):    
                if node.text:    
                    texts.append(node.text)    
        if texts:    
            paragraphs.append(''.join(texts))