Хобрук: Ваш путь к мастерству в программировании

Как можно заменить элемент текстом в lxml?

Полностью удалить заданный элемент из XML-документа с помощью lxml-реализации ElementTree API несложно, но я не вижу простого способа последовательно заменить элемент некоторым текстом. Например, учитывая следующий ввод:

input = '''<everything>
<m>Some text before <r/></m>
<m><r/> and some text after.</m>
<m><r/></m>
<m>Text before <r/> and after</m>
<m><b/> Text after a sibling <r/> Text before a sibling<b/></m>
</everything>
'''

... вы можете легко удалить каждый элемент <r> с помощью:

from lxml import etree
f = etree.fromstring(data)
for r in f.xpath('//r'):
    r.getparent().remove(r)
print etree.tostring(f, pretty_print=True)

Однако как бы вы заменили каждый элемент текстом, чтобы получить результат:

<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/>Text after a sibling DELETED Text before a sibling<b/></m>
</everything>

Мне кажется, что, поскольку API ElementTree работает с текстом через атрибуты .text и .tail каждого элемента, а не через узлы в дереве, это означает, что вам приходится иметь дело с множеством разных случаев в зависимости от того, есть ли у элемента родственные элементы или нет. , имел ли существующий элемент атрибут .tail и т. д. Я пропустил какой-то простой способ сделать это?

24.03.2011

  • Если у <r/> есть дети, их тоже удалить? Или слился с родителем <r/>? 24.03.2011
  • В этом случае я просто хочу удалить узел <r> и все его дочерние элементы и заменить его текстовой строкой. Надеюсь так проще :) 24.03.2011

Ответы:


1

Я думаю, что XSLT-решение unutbu, вероятно, является правильным способом достижения вашей цели.

Тем не менее, вот несколько хакерский способ добиться этого, изменив хвосты тегов <r/>, а затем используя etree.strip_elements.

from lxml import etree

data = '''<everything>
<m>Some text before <r/></m>
<m><r/> and some text after.</m>
<m><r/></m>
<m>Text before <r/> and after</m>
<m><b/> Text after a sibling <r/> Text before a sibling<b/></m>
</everything>
'''

f = etree.fromstring(data)
for r in f.xpath('//r'):
  r.tail = 'DELETED' + r.tail if r.tail else 'DELETED'

etree.strip_elements(f,'r',with_tail=False)

print etree.tostring(f,pretty_print=True)

Дает тебе:

<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/> Text after a sibling DELETED Text before a sibling<b/></m>
</everything>
24.03.2011
  • Спасибо, это хорошее решение - я не знал об strip_elements или with_tail optino 26.03.2011
  • Хотел придерживаться lxml для обработки html. Но, вероятно, переключится на Beautifulsoup, он гораздо более интуитивно понятен для базовой модификации html и может использовать lxml в качестве синтаксического анализатора... soup = BeautifulSoup(text, "lxml") / soup.find_all('r').replace_with('DELETED') 22.08.2018
  • Спасибо @benzkij за подсказку! Очень странно, что текст иногда рассматривается как хвост других узлов в ElementTree API, а не просто как обычный текстовый узел, как это предусмотрено xml. 01.10.2019

  • 2

    Использование strip_elements имеет тот недостаток, что вы не можете сохранить некоторые элементы <r> при замене других. Это также требует существования экземпляра ElementTree (что может быть не так). И, наконец, вы не можете использовать его для замены XML-комментариев или инструкций по обработке. Следующее должно сделать вашу работу:

    for r in f.xpath('//r'):
        text = 'DELETED' + r.tail 
        parent = r.getparent()
        if parent is not None:
            previous = r.getprevious()
            if previous is not None:
                previous.tail = (previous.tail or '') + text
            else:
                parent.text = (parent.text or '') + text
            parent.remove(r)
    
    09.05.2012
  • Я думаю, что text = 'DELETED' + r.tail должно быть text = 'DELETED' + r.tail if r.tail else 'DELETED'. 10.05.2012

  • 3

    Используя ET.XSLT:

    import io
    import lxml.etree as ET
    
    data = '''<everything>
    <m>Some text before <r/></m>
    <m><r/> and some text after.</m>
    <m><r/></m>
    <m>Text before <r/> and after</m>
    <m><b/> Text after a sibling <r/> Text before a sibling<b/></m>
    </everything>
    '''
    
    f=ET.fromstring(data)
    xslt='''\
        <xsl:stylesheet version="1.0"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform">    
    
        <!-- Replace r nodes with DELETED
             http://www.w3schools.com/xsl/el_template.asp -->
        <xsl:template match="r">DELETED</xsl:template>
    
        <!-- How to copy XML without changes
             http://mrhaki.blogspot.com/2008/07/copy-xml-as-is-with-xslt.html -->    
        <xsl:template match="*">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="@*|text()|comment()|processing-instruction">
            <xsl:copy-of select="."/>
        </xsl:template>
        </xsl:stylesheet>
    '''
    
    xslt_doc=ET.parse(io.BytesIO(xslt))
    transform=ET.XSLT(xslt_doc)
    f=transform(f)
    
    print(ET.tostring(f))
    

    урожаи

    <everything>
    <m>Some text before DELETED</m>
    <m>DELETED and some text after.</m>
    <m>DELETED</m>
    <m>Text before DELETED and after</m>
    <m><b/> Text after a sibling DELETED Text before a sibling<b/></m>
    </everything>
    
    24.03.2011
  • +1 Это хороший, но действительно неочевидный ответ :) Этот вопрос возник у меня из-за моего недостаточного ответ на другой вопрос, и я надеялся, что есть более простой способ. Даже с таким коротким примером, как этот, XSLT многословен и труден для понимания по сравнению с кодом в моем вопросе для простого удаления элементов. 24.03.2011
  • Новые материалы

    Почему я, журналист, в отчаянии создал сетевое приложение B2B
    Почему я, журналист, в отчаянии создал сетевое приложение B2B Итак, вот верхняя линия. Я построил OnGreentech, сеть для индустрии возобновляемых источников энергии. Если вам интересно,..

    Ограниченные машины Больцмана (RBM)
    Практическое руководство по обучению RBM.pdf Задний план RBM использовались в качестве генеративных моделей для многих различных типов данных, включая помеченные и немеченые. В их условной..

    Обнаружение маски или без маски с YOLO😷
    Это руководство по созданию пользовательской модели обнаружения объектов для обнаружения людей, носящих или не носящих маски в общественных местах, созданной с использованием YOLO v3. Модель..

    Управление приборами в чистом PHP
    Этот пост дополняет эти: E2e тестирование Работа с несколькими средами . Мы разработали это решение для базы данных MariaDB, оно может отличаться, если вы используете другую базу..

    Неделя 1 — Кентерберийские рельсы.
    Неделя 1 — Кентерберийские рельсы. So. Мы все еще живы, все еще усердно работаем и еще не пассивно-агрессивно рассылаем друг другу сообщения «за мое последнее сообщение в Slack…», поэтому, на..

    Цена завтрашнего дня  — Джефф Бут
    Технологический прогресс в наши дни происходит с молниеносной скоростью, и мы не в состоянии это понять. Джефф в основном говорит о влиянии технологий на экономику по всему миру. Он твердо верит..

    Данные: суперсила современного бизнеса
    В цифровой среде данные превратились из простого побочного продукта бизнес-операций в центральный актив, стимулирующий рост и инновации. Крейг Манди, бывший главный директор по стратегии..