2010-11-11 4 views
0

Моя задача состоит в том, чтобы сделать незначительные рефакторинга некоторых элементов XML дерева в Python 3, а именно заменить следующую структуру:Условное удаление элемента из 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> 

С:

<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 

Это один не проходит через все теги рекурсивно - если он соответствует что-то похожее на структуру она стремится - даже если это не удается, он не продолжает искать структуру внутри этих элементов, но в моем случае я не должен это делать (хотя это было бы неплохо иметь, но стоимость добавления кучи else: self .__ refactor_links (tag) kill это в моих глазах).

Если какое-либо условие не выполняется, удаление не должно происходить. Есть ли более чистый способ определить множество условий, избегая огромного набора «ifs»? Для сохранения условий может использоваться некоторая структура пользовательских данных, например. ('sup', ('img', (...))), но я не знаю, как его обрабатывать. Если у вас есть какие-либо предложения или примеры в python - пожалуйста, помогите.

Спасибо.

+0

Ouch. 'import this':' ... Flat лучше вложенного. ... ' –

ответ

1

Это определенно задача для выражения XPath, в вашем случае, вероятно, в сочетании с lxml.

XPath, вероятно, что-то вдоль линий:

//span[@class="nobr"]/a[@href]/sup[img/@class="rendericon"]

Подбирайте дерево с этим выражением XPath и удалить все соответствующие элементы. Нет необходимости в бесконечных конструкциях или рекурсии.

+0

Спасибо за указание на 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

1

Я не очень хорошо с 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> 
>>> 
0

Вот быстрая вещь с 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> 
0

легко осуществляется с помощью 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> 

Edit:

Я должен отметить, что ни один из ответов - не мое, не Matth, и, конечно, не пример 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(*).)

Смежные вопросы