Используя XPath, выберите узел без текстового брата

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

Рассмотрим этот HTML:

<!DOCTYPE html>
<html>
  <body>
    <span class="foo">
      <span class="bar">bar</span>
      foo
    </span>
  </body>
</html>

Рассмотрим эту программу:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from lxml import html
tree = html.fromstring('html from above')
bars = tree.xpath("//span[@class='bar']")
print(bars)
print(html.tostring(bars[0], encoding="unicode"))

В браузере селектор запросов span.bar выбирает только элемент span. Это то, чего я желаю. Однако приведенная выше программа производит:

[<Element span at 0x7f5dd89a4048>]
<span class="bar">bar</span>foo

Похоже, мой XPath на самом деле не ведет себя как селектор запросов, и текстовый узел-брат выбирается рядом с элементом span. Как мне настроить XPath, чтобы выбрать только элемент bar, но не текст «foo»?


person Hermann    schedule 26.02.2018    source источник


Ответы (1)


Обратите внимание, что модель дерева XML в lxml (а также в стандартном модуле xml.etree) имеет концепцию tail. Таким образом, текстовые узлы , расположенные после a.k.a following-sibling из элемента, будут сохранены как tail этого элемента. Таким образом, ваш XPath правильно возвращает элемент span, но, согласно модели дерева, он имеет tail, который содержит текст 'foo'.

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

>>> bars[0].tail = ''
>>> print(html.tostring(bars[0], encoding="unicode"))
<span class="bar">bar</span>
person har07    schedule 27.02.2018
comment
Понятие tail досадно противоречит обычным ожиданиям, основанным на XML и XPath, но это очень полезный ответ. - person kjhughes; 27.02.2018