条件移除元素的
我的任务就是做XML树的一些元素的微小的重构在Python 3,即替换以下结构:条件移除元素的
<span class="nobr">
<a href="http://www.google.com/">
http://www.google.com/
<sup>
<img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://jira.atlassian.com/icon.gif" width="7"/>
</sup>
</a>
</span>
有了:
<span class="nobr">
<a href="http://www.google.com/">
http://www.google.com/
</a>
</span>
即 - 如果整个结构与第一个例子中给出的结构完全一致,请移除sup元素。我需要在处理过程中保留XML文档,所以正则表达式匹配不是可能的。
我已经有代码的工作,我的目的:
doc = self.__refactor_links(doc)
...
def __refactor_links(self, node):
"""Recursively seeks for links to refactor them"""
for span in node.childNodes:
replace = False
if isinstance(span, xml.dom.minidom.Element):
if span.tagName == "span" and span.getAttribute("class") == "nobr":
if span.childNodes.length == 1:
a = span.childNodes.item(0)
if isinstance(a, xml.dom.minidom.Element):
if a.tagName == "a" and a.getAttribute("href"):
if a.childNodes.length == 2:
aurl = a.childNodes.item(0)
if isinstance(aurl, xml.dom.minidom.Text):
sup = a.childNodes.item(1)
if isinstance(sup, xml.dom.minidom.Element):
if sup.tagName == "sup":
if sup.childNodes.length == 1:
img = sup.childNodes.item(0)
if isinstance(img, xml.dom.minidom.Element):
if img.tagName == "img" and img.getAttribute("class") == "rendericon":
replace = True
else:
self.__refactor_links(span)
if replace:
a.removeChild(sup)
return node
这一次不会通过所有的标签递归地运行 - 如果它匹配相似,它寻求结构的东西 - 即使它失败,它不会继续寻找这些元素内部的结构,但在我的情况下,我不应该这样做(虽然这也会很好,但是增加一堆其他成本:self .__ refactor_links(tag)kill它在我眼中)。
如果任何条件失败,则不应该发生移除。有没有更清晰的方式来定义一组条件,避免大量'ifs'?一些自定义数据结构可以用于存储条件,例如, ('sup',('img',(...))),但我不知道应该如何处理它。如果你在Python中有任何建议或例子 - 请帮忙。
谢谢。
这绝对是XPath表达式的一个任务,在您的情况下可能与lxml一起使用。
的XPath可能是沿着线的东西:
//span[@class="nobr"]/a[@href]/sup[img/@class="rendericon"]
彰显树与此XPath表达式,并删除所有匹配的元素。 如果构造或递归没有必要。
感谢您指出XPath,从未使用它。我重写了所有的东西来使用xml.etree.ElementTree而不是xml.dom.minidom。 ElementTree 1.3支持我需要的所有XPath特性(http://effbot.org/zone/element-xpath.htm),所以我不得不切换到python 3.2(3.1的当前稳定版本有1.2.6)。 – DarkPhoenix 2010-11-12 14:58:17
我不擅长与XML,但不能使用节点上
>>> from xml.dom.minidom import parse, parseString
>>> dom = parseString(x)
>>> k = dom.getElementsByTagName('sup')
>>> for l in k:
... p = l.parentNode
... p.removeChild(l)
...
<DOM Element: sup at 0x100587d40>
>>>
>>> print dom.toxml()
<?xml version="1.0" ?><span class="nobr">
<a href="http://www.google.com/">
http://www.google.com/
</a>
</span>
>>>
下面是与lxml
快速事情查找/搜索。强烈推荐xpath
。
>>> from lxml import etree
>>> doc = etree.XML("""<span class="nobr">
... <a href="http://www.google.com/">
... http://www.google.com/
... <sup>
... <img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://jira.atlassian.com/icon.gif" width="7"/>
... </sup>
... </a>
... </span>""")
>>> for a in doc.xpath('//span[@class="nobr"]/a[@href="http://www.google.com/"]'):
... for sub in list(a):
... a.remove(sub)
...
>>> print etree.tostring(doc,pretty_print=True)
<span class="nobr">
<a href="http://www.google.com/">
http://www.google.com/
</a>
</span>
轻松使用lxml
和XSLT来实现:
>>> from lxml import etree
>>> from StringIO import StringIO
>>> # create the stylesheet
>>> xslt = StringIO("""
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- this is the standard identity transform -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- this replaces the specific node you're looking to replace -->
<xsl:template match="span[a[@href='http://www.google.com' and
sup[img[
@align='absmiddle' and
@border='0' and
@class='rendericon' and
@height='7' and
@src='http://jira.atlassian.com/icon.gif' and
@width='7']]]]">
<span class="nobr">
<a href="http://www.google.com/">http://www.google.com/</a>
</span>
</xsl:template>
</xsl:stylesheet>""")
>>> # create a transform function from the XSLT stylesheet
>>> transform = etree.XSLT(etree.parse(xslt))
>>> # here's a sample source XML instance for testing
>>> source = StringIO("""
<test>
<span class="nobr">
<a href="http://www.google.com/">
http://www.google.com/
<sup>
<img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://jira.atlassian.com/icon.gif" width="7"/>
</sup>
</a>
</span>
</test>""")
>>> # parse the source, transform it to an XSLT result tree, and print the result
>>> print etree.tostring(transform(etree.parse(source)))
<test>
<span class="nobr"><a href="http://www.google.com/">http://www.google.com/</a></span>
</test>
编辑:
我要指出,没有一个答案 - 不是我的,不是MattH的,当然不是实例OP张贴 - 做什么OP要求,这是只取代其结构正好匹配的元素
<span class="nobr">
<a href="http://www.google.com/">
http://www.google.com/
<sup>
<img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://jira.atlassian.com/icon.gif" width="7"/>
</sup>
</a>
</span>
例如,所有的这些例子将取代sup
如果img
有style
属性,或者如果sup
有除了img
另一个孩子。
构建XPath表达式可能会更加严格。例如,而不是使用
span[a]
与至少一个
a
孩子任何
span
匹配
,您可以使用
span[count(@*)=0 and count(*)=1 and a]
它不具有属性的任何span
和只有一个子元素,其中匹配那个孩子是a
。你可以去用这个漂亮的疯狂在你的追求精密,例如:
span[count(@*) = 1 and
@class='nobr' and
count(*) = 1 and
a[count(@*) = 1 and
@href='http://www.google.com' and
count(*) = 1 and
sup[count(@*) = 0 and
count(*) = 1 and
img[count(*) = 0 and
count(@*) = 7 and
@align='absmiddle' and
@alt='' and
@border='0' and
@class='rendericon' and
@height='7' and
@src='http://jira.atlassian.com/icon.gif' and
@width='7']]]]
其中,在匹配的每一步,确保元素匹配只包含完全相同的属性和指定的元素并没有更多的。 (并且它仍然不验证它们不包含文本,注释或处理指令 - 如果您确实严肃认真,请在任何地方使用count(node())
,这是使用count(*)
。)
Ouch。 'import this'':'... Flat比嵌套更好。 ...' – 2010-11-12 00:38:23