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)
Ответы
Ответ 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.