удалите все между двумя тегами, которые охватывают ветви дерева xml

Я пытаюсь удалить все в XML-документе между двумя тегами, используя python и lxml. проблема в том, что теги могут быть в разных ветвях дерева (но всегда на одной глубине) примерный документ может выглядеть так.

<root>
    <p> Hello world <start />this is a paragraph </p>
    <p> Goodbye world. <end />I'm leaving now </p>
</root>

я хотел бы удалить все между начальным и конечным тегами. что приведет к одному тегу p:

<root>
    <p> Hello world I'm leaving now </p>
</root>

Кто-нибудь знает, как это можно сделать с помощью lxml и python?


person user61000    schedule 04.08.2010    source источник


Ответы (3)


Вы можете попробовать использовать SAX-подобный интерфейс целевого парсера:

from lxml import etree

class SkipStartEndTarget:
    def __init__(self, *args, **kwargs):
        self.builder = etree.TreeBuilder()
        self.skip = False

    def start(self, tag, attrib, nsmap=None):
        if tag == 'start':
            self.skip = True
        if not self.skip:
            self.builder.start(tag, attrib, nsmap)

    def data(self, data):
        if not self.skip:
            self.builder.data(data)

    def comment(self, comment):
        if not self.skip:
            self.builder.comment(self)

    def pi(self, target, data):
        if not self.skip:
            self.builder.pi(target, data)

    def end(self, tag):
        if not self.skip:
            self.builder.end(tag)
        if tag == 'end':
            self.skip = False

    def close(self):
        self.skip = False
        return self.builder.close()

Затем вы можете использовать класс SkipStartEndTarget для создания parser target и создать собственный XMLParser с этой целью, например так:

parser = etree.XMLParser(target=SkipStartEndTarget())

Вы по-прежнему можете предоставить парсеру другие параметры парсера, если они вам нужны. Затем вы можете предоставить этот синтаксический анализатор используемой вами функции синтаксического анализатора, например:

elem = etree.fromstring(xml_str, parser=parser)

Это также работает с etree.XML() и etree.parse(), и вы даже можете установить синтаксический анализатор в качестве синтаксического анализатора по умолчанию с etree.setdefaultparser() (что, вероятно, не очень хорошая идея). Одна вещь, которая может сбить вас с толку: даже с etree.parse() это не вернет elementtree, а всегда будет элементом (как это делают etree.XML() и etree.fromstring()). Я не думаю, что это можно сделать (пока), поэтому, если это проблема для вас, вам придется как-то ее обойти.

Обратите внимание, что также можно использовать создание дерева элементов из событий sax с lxml.sax, что, вероятно, несколько сложнее и медленнее. В отличие от приведенного выше примера, он вернет elementtree, но я думаю, что он не предоставляет .docinfo, который вы получили бы при обычном использовании etree.parse(). Я также считаю, что он (в настоящее время) не поддерживает комментарии и пи. (еще не пользовался, поэтому точнее сказать не могу)

Также обратите внимание, что любой SAX-подобный подход к синтаксическому анализу документа требует, чтобы пропуск всего между <start/> и <end/> по-прежнему приводил к правильно сформированному документу, что имеет место в вашем примере, но не было бы так, если бы второй <p> был <p2>, например, так как в итоге получится <p>....</p2>.

person Steven    schedule 04.08.2010

У вас на руках беспорядок, и вы должны дать пощечину человеку, который написал преднамеренное искажение правила вложенности XML.

Вероятно, лучше всего использовать что-то вроде SAX, чтобы распознать тег <start/> и начать отбрасывая ввод, пока вы не нажмете <end/>. SAX имеет здесь преимущество перед lxml, потому что он позволяет вам выполнять произвольные действия для каждой лексемы, в то время как lxml уже разделит начало и конец, прежде чем вы их коснетесь.

Пока вы это делаете, вы можете преобразовать эти документы в пригодный для использования XML.

person msw    schedule 04.08.2010
comment
о, как бы я хотел что-нибудь с этим сделать... это файл ODT. они используют их для отслеживания изменений, к сожалению, я делаю много других манипуляций с файлом, используя etree, поэтому я не уверен, смогу ли я переключиться на SAX :( рад узнать, что он справится с этим, хотя мне может понадобиться загляни в него. - person user61000; 04.08.2010

Я знаю, что есть люди, которые захотят побить меня камнями за это, но вы можете просто использовать регулярное выражение:

import re
new_string = re.sub(r'<start />(.*?)<end />', '', your_string, re.S)

Вы не можете использовать синтаксический анализатор XML, если он не является допустимым XML.

person NullUserException    schedule 04.08.2010
comment
Я склонен согласиться. Хотя в некоторых случаях это может быть менее эффективным, вероятно, это не вызовет серьезных проблем с производительностью. - person William Niu; 04.08.2010
comment
XML полностью действителен. обратите внимание, что начало и конец являются полными самозакрывающимися тегами. Я думал о маршруте регулярных выражений, но документ огромен, и мне нужно удалить много таких случаев. - person user61000; 04.08.2010
comment
@ user61 Вы правы, это действительный XML. Я не знаю лучшего слова. Может быть, это неправильный XML? В любом случае, если вы можете прочитать его в режиме slurp, то, вероятно, у вас все будет хорошо, независимо от размера. - person NullUserException; 04.08.2010
comment
@NullUserException похоже, что это путь, по которому мне придется идти, но в том же сценарии я делаю много других манипуляций с документом, и я не уверен, как это можно сделать в slurp режим, который я никогда не использовал раньше. - person user61000; 04.08.2010
comment
@NullUserException, извините, я думал, вы имеете в виду использование SAX или что-то в этом роде. единственная проблема с методом регулярных выражений заключается в том, что у меня есть список операций (вставки и удаления), которые мне нужно выполнить с документом, которые необходимо применить в правильном порядке, поэтому мне придется сбросить весь документ в строку и повторно анализировать ее полностью для каждого отдельного удаления, которых в одном документе могут быть сотни. - person user61000; 04.08.2010