Легко выполнить, используя 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>
Редактировать:
Я должен отметить, что ни один из ответов - ни мой, ни МэттХи, конечно, не опубликованный пример 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]
, который соответствует любому span
хотя бы с одним a
дочерним элементом, вы можете использовать
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(*)
.)