Как получить полное содержимое XML или HTML элемента с помощью ElementTree?

То есть весь текст и вложенные теги без тега самого элемента?

Имея

<p>blah <b>bleh</b> blih</p>

я хочу

blah <b>bleh</b> blih

element.text возвращает "blah", а etree.tostring (element) возвращает:

<p>blah <b>bleh</b> blih</p>
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
11
0
7 510
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

ElementTree работает отлично, ответ нужно собрать самостоятельно. Что-то вроде этого...

"".join( [ "" if t.text is None else t.text ] + [ xml.tostring(e) for e in t.getchildren() ] )

Спасибо СП amd PEZ за указание на ошибки.


Редактировать.

>>> import xml.etree.ElementTree as xml
>>> s= '<p>blah <b>bleh</b> blih</p>\n'
>>> t=xml.fromstring(s)
>>> "".join( [ t.text ] + [ xml.tostring(e) for e in t.getchildren() ] )
'blah <b>bleh</b> blih'
>>> 

Хвост не нужен.

Просто указываю на опечатку - имя метода - «финал», которое, я думаю, должно было быть «findall». Даже если используется findall, это приводит к этому pastebin.com/f6de9a841. Пожалуйста, измените свой ответ.

JV. 19.12.2008 14:45

Я делаю что-то похожее на это, но с интересом. Вам действительно не хватает хвоста.

pupeno 19.12.2008 20:26

Хвост - это лишний пробел после закрывающего тега конструкции.

S.Lott 22.01.2010 23:24

Не знаю, может ли внешняя библиотека быть вариантом, но в любом случае - если на странице есть один <p> с этим текстом, jQuery-решение будет:

alert($('p').html()); // returns blah <b>bleh</b> blih

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

 re.sub(r'(^<%s\b.*?>|</%s\b.*?>$)' % (element.tag, element.tag), '', ElementTree.tostring(element))
Ответ принят как подходящий

Это решение, которое я в итоге использовал:

def element_to_string(element):
    s = element.text or ""
    for sub_element in element:
        s += etree.tostring(sub_element)
    s += element.tail
    return s

Это не сработает, если нет текста или хвоста, не так ли?

PEZ 19.12.2008 23:35

PEZ, да, он не работает, когда нет текста, просто нашел его, запустив мой код, и исправил. У меня много примеров отсутствия хвоста, и это не подводит меня. Не знаю почему.

pupeno 20.12.2008 20:02

Просто придирка: + = на струнах менее эффективен. Лучше всего собрать список строк и присоединить к нему "." В конце.

cdleary 21.12.2008 01:36

Вы можете выполнить рекурсию и снова вызвать element_to_string для подэлемента, чтобы захватить весь текст, то есть for sub_element in element: s += element_to_string(sub_element).

dbader 14.11.2015 22:43

Это хорошие ответы, которые отвечают на вопрос OP, особенно если вопрос ограничен HTML. Но документы изначально беспорядочные, и глубину вложенности элементов обычно невозможно предсказать.

Чтобы смоделировать getTextContent () DOM, вам нужно будет использовать (очень) простой рекурсивный механизм.

Чтобы получить только простой текст:

def get_deep_text( element ):
    text = element.text or ''
    for subelement in element:
        text += get_deep_text( subelement )
    text += element.tail or ''
    return text
print( get_deep_text( element_of_interest ))

Чтобы получить все подробности о границах необработанного текста:

root_el_of_interest.element_count = 0
def get_deep_text_w_boundaries( element, depth = 0 ):
    root_el_of_interest.element_count += 1
    element_no = root_el_of_interest.element_count 
    indent = depth * '  '
    text1 = '%s(el %d - attribs: %s)\n' % ( indent, element_no, element.attrib, )
    text1 += '%s(el %d - text: |%s|)' % ( indent, element_no, element.text or '', )
    print( text1 )
    for subelement in element:
        get_deep_text_w_boundaries( subelement, depth + 1 )
    text2 = '%s(el %d - tail: |%s|)' % ( indent, element_no, element.tail or '', )
    print( text2 )
get_deep_text_w_boundaries( root_el_of_interest )

Пример вывода одного параграфа в документе LibreOffice Writer (файл .fodt):

(el 1 - attribs: {'{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name': 'Standard'})
(el 1 - text: |Ci-après individuellement la "|)
  (el 2 - attribs: {'{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name': 'T5'})
  (el 2 - text: |Partie|)
  (el 2 - tail: |" et ensemble les "|)
  (el 3 - attribs: {'{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name': 'T5'})
  (el 3 - text: |Parties|)
  (el 3 - tail: |", |)
(el 1 - tail: |
   |)

Один из моментов, связанных с беспорядком, заключается в том, что не существует жесткого правила о том, когда стиль текста указывает границу слова, а когда нет: надстрочный индекс, следующий сразу за словом (без пробелов), означает отдельное слово во всех случаях использования, которые я могу представить. OTOH иногда вы можете найти, например, документ, в котором первая буква по какой-то причине выделена жирным шрифтом или, возможно, использует другой стиль для первой буквы, чтобы представить ее в верхнем регистре, а не просто использовать обычный символ UC.

И, конечно же, чем менее «англо-центричным» эта дискуссия, тем больше тонкостей и сложностей!

Большинство ответов здесь основаны на анализаторе XML ElementTree, даже Ответ на основе регулярных выражений PEZ по-прежнему частично полагается на ElementTree.

Все они хороши и подходят для большинства случаев использования, но для полноты картины стоит отметить, что ElementTree.tostring(...) предоставит вам эквивалентный фрагмент, но не всегда идентичный исходной полезной нагрузке. Если по какой-то очень редкой причине вы хотите извлечь контент как есть, вам нужно использовать решение на основе чисто регулярных выражений. Этот пример - это то, как я использую решение на основе регулярных выражений.

Этот ответ немного изменен из ответа Пупено. Здесь я добавил тип кодировки в «tostring». На этот вопрос у меня ушло много часов. Надеюсь, это небольшое исправление поможет другим.

def element_to_string(element):
        s = element.text or ""
        for sub_element in element:
            s += ElementTree.tostring(sub_element, encoding='unicode')
        s += element.tail
        return s

Другие вопросы по теме