在XML中解析模板标签的正则表达式

3

我需要解析一些XML以提取嵌入的模板标签以便进行进一步解析。然而,我似乎无法通过Python的正则表达式来实现我想要的功能。

简而言之,当一个模板标签包含在行中的任何位置时,删除该行的所有XML,只保留模板标签。

我编写了一个测试用例来演示。以下是原始XML:

<!-- regex_trial.xml -->
<w:tbl>
    <w:tr>
        <w:tc><w:t>Header 1</w:t></w:tc>
        <w:tc><w:t>Header 2</w:t></w:tc>
        <w:tc><w:t>Header 3</w:t></w:tc>
    </w:tr>
     <w:tr>
        <w:tc><w:t>{% for i in items %}</w:t></w:tc>
        <w:tc><w:t></w:t></w:tc>
        <w:tc><w:t></w:t></w:tc>
    </w:tr>
    <w:tr>
        <w:tc><w:t>{{ i.field1 }}</w:t></w:tc>
        <w:tc><w:t>{{ i.field2 }}</w:t></w:tc>
        <w:tc><w:t>{{ i.field3 }}</w:t></w:tc>
    </w:tr>
    <w:tr>
        <w:tc><w:t>{% endfor %}</w:t></w:tc>
        <w:tc><w:t></w:t></w:tc>
        <w:tc><w:t></w:t></w:tc>
    </w:tr>
</w:tbl>

这是所期望的结果:

这是期望的结果:

<!-- regex_desired_result.xml -->
<w:tbl>
    <w:tr>
        <w:tc><w:t>Header 1</w:t></w:tc>
        <w:tc><w:t>Header 2</w:t></w:tc>
        <w:tc><w:t>Header 3</w:t></w:tc>
    </w:tr>
    {% for i in items %}
    <w:tr>
        <w:tc><w:t>{{ i.field1 }}</w:t></w:tc>
        <w:tc><w:t>{{ i.field2 }}</w:t></w:tc>
        <w:tc><w:t>{{ i.field3 }}</w:t></w:tc>
    </w:tr>
    {% endfor %}
</w:tbl>

这是我正在使用的一些Python代码进行测试:

#!/usr/bin/env python
import re
f = open( 'regex_trial.xml', 'r' )
orig_xml = f.read()
f.close()
p = re.compile( '<w:tr.*?(?P<tag>{%.*?%}).*?</w:tr>', re.DOTALL )
new_xml = p.sub( '\g<tag>', orig_xml, 0 )
print new_xml

这个正则表达式的实际结果是:

<!-- regex_trial.xml -->
<w:tbl>
    {% for i in items %}
    {% endfor %}
</w:tbl>

任何帮助都将不胜感激!如果我们能解决这个问题,我们就能够从Django-powered网站动态生成MS Word docx文件。谢谢!更新:这是我使用的最终代码。
from xml.etree import ElementTree
import cStringIO as StringIO

TEMPLATE_TAG = 'template_text'

tree = ElementTree.parse( 'regex_trial.xml' )
rows = tree.getiterator('tr')
for row in rows:
    for cell in row.getiterator('t'):
        if cell.text and cell.text.find( '{%' ) >= 0:
            template_tag = cell.text
            row.clear()
            row.tag = TEMPLATE_TAG
            row.text = template_tag
            break

output = StringIO.StringIO()
tree.write( output )
xml = output.getvalue()
xml = xml.replace('<%s>' % TEMPLATE_TAG, '')
xml = xml.replace('</%s>' % TEMPLATE_TAG, '')
print xml

感谢您的所有帮助!

2
@user425130 你考虑过使用 XML/XSLT 解析器吗? - Oscar Mederos
感谢参与!我考虑过使用XSL,但那变得相当混乱 - 你必须检查每个tr,需要有一个xpath表达式来检查子tc的内容。然后你还得处理我们不想处理的其他99%的内容。唉。 - aeb6
@user425130,+1个很棒的解决方案!太棒了! - Mike Pennington
1
+1:非常棒的第一个问题! - jathanism
2个回答

4
请不要使用正则表达式解决这个问题。
我是认真的,使用正则表达式解析XML很困难,并且会使您的代码难以被其他人维护50倍。 lxml是Python程序员用来解析XML的事实标准工具...请查看Stack Overflow上的本文以获取示例用法。或者考虑这个答案, 这应该是被接受的答案。
我快速创建了一个演示...它搜索具有非空<w:t>子元素的<w:tc>并在每个元素旁边打印good。
import lxml.etree as ET
from lxml.etree import XMLParser

def worthy(elem):
    for child in elem.iterchildren():
        if (child.tag == 't') and (child.text is not None):
            return True
    return False

def dump(elem):
    for child in elem.iterchildren():
        print "Good", child.tag, child.text

parser = XMLParser(ns_clean=True, recover=True)
etree = ET.parse('regex_trial.xml', parser)
for thing in etree.findall("//"):
    if thing.tag == 'tc' and worthy(thing):
        dump(thing)

产量...

Good t Header 1
Good t Header 2
Good t Header 3
Good t {% for i in items %}
Good t {{ i.field1 }}
Good t {{ i.field2 }}
Good t {{ i.field3 }}
Good t {% endfor %}

+1:如果你想在标准库范围内选择,xml.etree.ElementTree 也是一个合适的选择。 - jathanism
@jathanism 谢谢你指出这一点,我一直使用 lxml - Mike Pennington
有关如何使用 XML 库完成这个任务的任何建议吗?感觉我需要检查每个 <w:tr> 节点,看它是否包含任何包含 "{%*%}" 的节点,然后用子节点的内容替换 <w:tr>。这开始变得复杂了。此外,我需要保留文档的其余部分(超过99%),这就是为什么我选择了查找/替换路径的原因。如果有任何示例,将非常有帮助。谢谢! - aeb6
或者,也许我应该遍历<w:tr>元素,然后对元素的文本使用正则表达式来确定是否需要替换。不过,我还不确定如何处理保留文档的其余部分。 - aeb6
感谢你们的所有帮助 - 使用 XML 解析器绝对是正确的方法。请参阅我上面的更新以获取最终代码。 - aeb6

3

永远不要使用正则表达式解析HTML、XML或SGML。

始终使用像lxml、libxml2或Beautiful这样的工具——它们将比你的代码做得更聪明、更好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接