удалить все между 2 тегами, которые охватывают ветви дерева XML - PullRequest
1 голос
/ 04 августа 2010

Я пытаюсь удалить все в XML-документе между 2 тегами, используя 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?

Ответы [ 3 ]

1 голос
/ 04 августа 2010

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

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

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

1 голос
/ 04 августа 2010

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

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

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

0 голосов
/ 04 августа 2010

Вы можете попробовать использовать 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() это не вернет дерево элементов, но всегда элемент (как это делают etree.XML() и etree.fromstring()).Я не думаю, что это может быть сделано (пока), поэтому, если это проблема для вас, вам придется как-то обойти это.

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

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

...