2010-09-10 4 views
4

Предположим, что у меня есть такого рода HTML, из которого нужно выбрать «text2» с помощью LXML/ElementTree:Получение несмежных текста с LXML/ElementTree

<div>text1<span>childtext1</span>text2<span>childtext2</span>text3</div> 

Если у меня уже есть Div элемент, как mydiv, то mydiv.text возвращает только «text1».

Использование itertext() кажется проблематичным или громоздким в лучшем случае, поскольку оно проходит все дерево под div.

Есть ли простой/изящный способ извлечь не первый фрагмент текста из элемента?

+1

Это похоже на ошибку. Вы пытались использовать 'findtext (path)'? –

+2

Поскольку мой ответ, по-видимому, не отвечает на ваш вопрос, не могли бы вы объяснить, что именно вы ищете? – llasram

ответ

12

Ну, lxml.etree предоставляет полный XPath поддержка, которая позволяет решать текстовые элементы:

>>> import lxml.etree 
>>> fragment = '<div>text1<span>childtext1</span>text2<span>childtext2</span>text3</div>' 
>>> div = lxml.etree.fromstring(fragment) 
>>> div.xpath('./text()') 
['text1', 'text2', 'text3'] 
+0

doc.xpath ('/ div/text()') [1: 2] предоставит вам не первый текст из элемента – shahjapan

+1

Ну, «способ извлечь не первый текстовый фрагмент» включает в себя множество возможностей после того, как вы получите все текстовые элементы в виде списка, например 'random.choice (div.xpath ('./ text()') [1:])' или 'set (div.xpath ('./ text()') [1: ]). поп() '. Однако, поскольку ОП знает достаточно, чтобы спросить о lxml, я считаю, что манипуляция списком - это небольшие бобы. –

6

Такой текст будет в атрибутах tail детей вашего элемента. Если элемент был в elem тогда:

elem[0].tail 

Даст вам хвост текст первого ребенка внутри элемента, в вашем случае "text2" вы ищете.

4

Как сказал llasram, любой текст не в атрибуте text будет в атрибутах tail дочерних узлов.

В качестве примера, вот самый простой способ извлечь все из блоков текста (первый и в противном случае) в узле:

html = '<div>text1<span>childtext1</span>text2<span>childtext2</span>text3</div>' 

import lxml.html # ...or lxml.etree as appropriate 
div = lxml.html.fromstring(html) 

texts = [div.text] + [child.tail for child in div] 
# Result: texts == ['text1', 'text2', 'text3'] 
# ...and you are guaranteed that div[x].tail == texts[x+1] 
# (which can be useful if you need to access or modify the DOM) 

Если вы не хотите принести в жертву, что отношения в целях предотвращения texts от потенциально содержащих пустые строк, вы можете использовать вместо этого:

texts = [div.text] + [child.tail for child in div if child.tail] 

Я не проверял это с простым старым STDLIB ElementTree, но он должен работать с этим тоже. (Что-то, что только произошло со мной, когда я увидел решение, специфичное для lxml Шейна Холлоуэя), я просто предпочитаю LXML, потому что он получил лучшую поддержку идеографических схем HTML, и я обычно уже устанавливал его для lxml.html.clean

1

Используйте node.text_content(), чтобы получить весь текст ниже узла в виде одной строки.

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