Python: как получить необработанный текст из элемента lxml - PullRequest
1 голос
/ 11 октября 2019

Я хочу получить следующие встроенные текстовые строки из корневого элемента.

from lxml import etree

root = root = etree.fromstring(
'''<p>
    text-first
    <span>
        Child 1
    </span>
    text-middle
    <span>
        Child 2
    </span>
    text-last
</p>''')

root.text возвращает только «text-first», включая новые строки

>>> build_text_list = etree.XPath("//text()")

>>> texts = build_text_list(root)
>>>
>>> texts
['\n    text-first\n    ', '\n        Child 1\n    ', '\n    text-middle\n    ', '\n        Child 2\n    ', '\n    text-last\n']
>>>
>>> for t in texts:
...     print t
...     print t.__dict__
...

    text-first

{'_parent': <Element p at 0x10140f638>, 'is_attribute': False, 'attrname': None, 'is_text': True, 'is_tail': False}

        Child 1

{'_parent': <Element span at 0x10140be18>, 'is_attribute': False, 'attrname': None, 'is_text': True, 'is_tail': False}

    text-middle

{'_parent': <Element span at 0x10140be18>, 'is_attribute': False, 'attrname': None, 'is_text': False, 'is_tail': True}

        Child 2

{'_parent': <Element span at 0x10140be60>, 'is_attribute': False, 'attrname': None, 'is_text': True, 'is_tail': False}

    text-last

{'_parent': <Element span at 0x10140be60>, 'is_attribute': False, 'attrname': None, 'is_text': False, 'is_tail': True}
>>>
>>> root.xpath("./p/following-sibling::text()") # following https://stackoverflow.com/a/39832753/1677041
[]

Итак,Как я могу получить text-first/middle/last частей от этого?

Есть идеи? Спасибо!

Ответы [ 4 ]

1 голос
/ 11 октября 2019

Ваше первоначальное предположение, //text() означает: выбрать все текстовые узлы, независимо от того, где они находятся в документе. Что вы на самом деле хотели бы выбрать, так это текстовые узлы, если они являются непосредственными дочерними элементами p, или, в качестве альтернативы, если они не являются дочерними элементами span.

Самый точный ответ с учетом входного документа, который вы показываетеis /p/text():

>>> root = etree.fromstring(
'''<p>
text-first
<span>
    Child 1
</span>
text-middle
<span>
    Child 2
</span>
text-last
</p>''')

>>> etree.XPath("/p/text()")(root)
['\n    text-first\n    ', '\n    text-middle\n    ', '\n    text-last\n']

Ваше собственное решение child::text() означает: выберите текстовые узлы, если они являются дочерними по отношению к текущему контекстному узлу. Это работает, потому что выражения XPath в этом случае оцениваются с использованием корневого элемента p в качестве контекста. Вот почему просто text() также работает.

>>> etree.XPath("text()")(root)
['\n    text-first\n    ', '\n    text-middle\n    ', '\n    text-last\n']
1 голос
/ 11 октября 2019

etree прекрасно на это способен:

from lxml import etree

root: etree.Element = etree.fromstring(
'''<p>
    text-first
    <span>
        Child 1
    </span>
    text-middle
    <span>
        Child 2
    </span>
    text-last
</p>''')

print(
    root.text,
    root[0].tail,
    root[1].tail,
)

Все элементы являются списками своих подэлементов, поэтому здесь индексация относится к элементам 2 <span>. Атрибут tail любого элемента содержит текст непосредственно после этого элемента.

Он, конечно, будет содержать символы новой строки, поэтому вы можете захотеть удалить () результаты: root.text.strip()

0 голосов
/ 11 октября 2019
print(root.xpath('normalize-space(//*)'))
0 голосов
/ 11 октября 2019

Мой плохой, xpath спасает меня в конце концов.

>>> root.xpath('child::text()')
['\n    text-first\n    ', '\n    text-middle\n    ', '\n    text-last\n']
...