Подтвердить что ты не робот

HTML XPath: Извлечение текста, смешанного с несколькими тегами?

Цель: извлечь текст из определенного элемента (например, li), игнорируя различные смешанные теги, т.е. сгладить дочерний уровень первого уровня и просто вернуть конкатенированный текст каждого сплющенного потомка отдельно.

Пример:

<div id="mw-content-text"><h2><span class="mw-headline" >CIA</span></h2>
    <ol>
    <li>Central <a href="/Intelligence_Agency.html">Intelligence Agency</a>.</li>
    <li>Culinary <a href="/Institute.html">Institute</a> of <a href="/America.html">America</a>.</li>
    </ol>

    </Div>  

желаемый текст:

  • Центральное разведывательное управление
  • Кулинарный институт Америки

За исключением того, что привязанные метки не позволяют получить простой поиск.

Чтобы вернуть каждый тег li отдельно, мы используем просто:

//div[contains(@id,"mw-content-text")]/ol/li

но также включает в себя окружающие метки привязки и т.д. И

//div[contains(@id,"mw-content-text")]/ol/li/text()

возвращает только текстовые элементы, которые являются прямыми дочерними элементами li, т.е. "Central", "."...

Казалось логичным тогда искать текстовые элементы себя и потомков

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text]

но ничего не возвращает ничего!

Любые предложения? Я использую Python, поэтому я открыт для использования других модулей для последующей обработки.

(Я использую Scrapy HtmlXPathSelector, который кажется совместимым с XPath 1.0)

4b9b3361

Ответ 1

Ты был почти там. В есть небольшая проблема:

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text]

Исправленное выражение:

//div[contains(@id,"mw-content-text")]/ol/li[descendant-or-self::text()]

Тем не менее, существует более простое выражение, которое создает точно необходимое объединение всех текстовых узлов под указанным li:

string(//div[contains(@id,"mw-content-text")]/ol/li)

Ответ 2

Я думаю, что следующее вернет правильный результат:

//div[contains(@id,"mw-content-text")]/ol/li//text()

Обратите внимание на двойную косую черту перед текстом(). Это означает, что текстовые узлы на любом уровне ниже li должны быть возвращены.

Ответ 3

Конкатенация строк сложна. Здесь быстрое решение с использованием lxml:

>>> from lxml import etree
>>> doc = etree.HTML("""<div id="mw-content-text"><h2><span class="mw-headline" >CIA</span></h2>
...     <ol>
...     <li>Central <a href="/Intelligence_Agency.html">Intelligence Agency</a>.</li>
...     <li>Culinary <a href="/Institute.html">Institute</a> of <a href="/America.html">America</a>.</li>
...     </ol>
...
...     </Div>""")
>>> for element in doc.xpath('//div[@id="mw-content-text"]/ol/li'):
...   print "".join(element.xpath('descendant-or-self::text()'))
...
Central Intelligence Agency.
Culinary Institute of America.

Обратите внимание, что // имеет потенциально плохую производительность/непреднамеренное выполнение, и его следует избегать, когда это возможно, но это трудно сделать с помощью фрагмента HTML.