Как получить братьев и сестер при использовании Содержит (text (),) в xpath - PullRequest
3 голосов
/ 10 февраля 2012

Я познакомился с xpath сегодня, и он кажется очень мощным, но после немалых поисков я не нашел, как получить братьев и сестер (с помощью follow-sibling и previous-sibling), когда используется содержимое:

text = """
<html>
  <head>
    <title>This tag includes 'some_text'</title>
    <h2>A h2 tag</h2>
  </head>
</html>
"""

import lxml.html
doc = lxml.html.fromstring(text)
a = doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")

, который производит [].Конечно, я ожидаю получить тег h2.

Однако, используя *[contains(text(),'name')], вы получите, как и ожидалось, элемент title.Таким же образом, если вместо использования оси следующего брата (я думаю, что так она называется), я использую //parent::*, также работает.

Итак, как я могу получить братьев и сестер при этом условии?

Заранее спасибо.

Ответы [ 4 ]

7 голосов
/ 10 февраля 2012

Забавный образец HTML у вас есть.

import lxml

text = """                                                       
<html>
  <body>
    <span>This tag includes 'some_text'</span>
    <h2>A h2 tag</h2>
  </body>
</html>
"""

doc = lxml.etree.fromstring(text, parser=lxml.etree.HTMLParser())
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")
# [<Element h2 at 102eee100>]

doc = lxml.html.fromstring(text)
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")
# [<Element h2 at 102f6f188>]

ОБНОВЛЕНИЕ:

Здесь я не использую парсер html с его правилами валидации и трактую ввод как случайныйxml:

text = """                       
<html>
  <head>
    <title>This tag includes 'some_text'</title>
    <h2>A h2 tag</h2>
  </head>
</html>
"""
doc = lxml.etree.fromstring(text)
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*[1]")
# [<Element h2 at 102eeef70>]
1 голос
/ 10 февраля 2012
<?xml version="1.0" ?>
  <html>
    <head>
      <title>This tag includes 'some_text'</title>
      <h2>A h2 tag</h2>
    </head>
  </html>
//*[contains(text(),'some_text')]/following-sibling::*
Array
(
    [0] => SimpleXMLElement Object
        (
            [0] => A h2 tag
        )

)

Я использовал PHP SimpleXMLElement, но xpath должен быть таким же.

1 голос
/ 10 февраля 2012

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

  1. follow-sibling вернет ВСЕ последующие братья, а не только непосредственные. Поэтому, если после этого есть узлы, они также будут возвращены.
  2. HTML не является XML. Хотя LXML попытается очистить исходный код для вас, если вы не можете доверять чистоте входящего HTML-кода, ваши XPath-файлы могут потерпеть неудачу. Например. Я полагаю, что теги заголовков не нуждаются в закрывающих тегах в HTML, поэтому в зависимости от того, насколько поврежден источник, LXML может неправильно поставить дочерний элемент, что может нарушить XPath
  3. У заголовков не может быть дочерних элементов, что может влиять на то, как LXML очищает их (например, добавление тега тела между ними и т. Д.).

Проверка этого в редакторе XML показывает, что ваш XPath действителен, но я испытывал недостаток элементов при тестировании в LXML, что может означать, что он каким-то образом изменяет XML (но я не проверял).

Я бы порекомендовал переосмыслить, если XPath является инструментом для этой работы, особенно если вы пытаетесь использовать его для подкачки веб-страниц или аналогичного.

Вы также можете подумать о переписывании оператора XPath, чтобы он также был немного более читабельным.

//*[contains(text(),'some_text')]/following-sibling::*

Это говорит: найдите мне любой элемент, у которого есть «некоторый текст» в тексте, затем найдите следующих его братьев и сестер.

//*[preceding-sibling::*[position()=1 and contains(text(),'some_text') and ]]

В то время как это говорит: найдите мне элемент, у которого у первого предыдущего родного брата есть текст, который содержит "некоторый текст".

Это может быть проблема стиля, но я нахожу последнее более читабельным.

0 голосов
/ 10 февраля 2012

Ключевым моментом здесь является то, что ваш XPath смотрит на дерево, созданное парсером HTML5, а не парсером XML. Анализаторы HTML5 создают узлы в дереве, которые не являются явными в вашем источнике: фактически они восстанавливают недопустимый HTML и превращают его в действительный HTML. Это влияет на любую попытку навигации по дереву HTML, независимо от того, используете ли вы XPath, JQuery или прямые API DOM.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...