R xpathSApply: получить текст узла, не получая текст от его дочерних элементов. - PullRequest
1 голос
/ 01 декабря 2011

Я пытаюсь извлечь текст из части сайта. Узел div, который содержит текст, также содержит несколько дочерних элементов, каждый из которых имеет свой собственный текст или другое содержимое. Однако я хочу, чтобы текст только из верхнего узла не принадлежал его дочерним элементам!

Вот так выглядит соответствующий раздел страницы:

    <div class="body-text">
       <div id="other" class="other"></div>
       <div id="other2" class="other2"></div>
       <div id="other3" class="other3"> 
           <span>irrelevant text</span>
        </div>

       <h2>heading2</h2>

       -Text which I want to get. There are also text parts which are linked.

    </div>

Это мой код, который дает мне "грязный" текст. Я попытался /text(), но это обрезает мой текст всякий раз, когда его часть связана. Поэтому я не могу это использовать. Я также пробовал что-то с /div/node()[not(self::div)], но не смог заставить его работать. Кто-нибудь может помочь?

webpage = getURL(url)
webpage <- readLines(tc <- textConnection(webpage)); close(tc)
pagetree <- htmlTreeParse(webpage, useInternalNodes = TRUE, encoding='UTF-8')

body <- xpathSApply(pagetree, "//div[@class='body-text']", xmlValue)

Ответы [ 2 ]

4 голосов
/ 01 декабря 2011

1) Прикрепленный пример

Попробуйте найти узлы text() или a/text() в пределах деления body-text, удалив все тривиальные узлы, которые содержат только пробел:

## input

Text <- '<div class="body-text">
       <div id="other" class="other"></div>
       <div id="other2" class="other2"></div>
       <div id="other3" class="other3"> 
           <span>irrelevant text</span>
        </div>
       <h2>heading2</h2>
       -Text which I want to get. There are also text parts which are linked.
    </div>'

library(XML)
pagetree <- htmlTreeParse(Text, asText = TRUE, useInternalNodes = TRUE)

## process it - xpth is the Xpath expression and xpathSApply() runs it

trim <- function(x) gsub("^\\s+|\\s+$", "", x) # trim whitespace from start & end

xpth <- "( //div[@class='body-text']/text() | 
   //div[@class='body-text']/a/text() ) [ normalize-space() != '' ]"
txt <- trim(xpathSApply(pagetree, xpth, xmlValue))

Результат следующий:

> txt
[1] "-Text which I want to get. There are also text parts which are linked."

2) Пример предоставлен постером в комментариях . Используя это как Text

Text <- '<div class="body-text"> text starts here 
 <a class="footnote" href="link"> text continues here <sup>1</sup> </a> 
 and continues here</div>'

и, повторяя приведенный выше код, получаем:

> txt
[1] "text starts here"    "text continues here" "and continues here" 

РЕДАКТИРОВАТЬ: были изменены выше на основе комментариев автора. Основным изменением было выражение xpath, xpth и последняя точка, которая иллюстрирует тот же код с примером, представленным в комментариях постером.

РЕДАКТИРОВАТЬ: переместил фильтрацию из узлов только для пробелов из R в Xpath. Это немного удлиняет выражение Xpath, но устраняет шаг R Filter (). Также немного упростили и сократили презентацию.

2 голосов
/ 02 декабря 2011

Существует несколько возможных решений этой проблемы, но, во-первых, необходимо уточнить, какие узлы вы хотите выбрать. Вы говорите:

Мне нужен только текст из верхнего узла, а не его потомков!

Но это не так! Все узлы элементов, найденные в тексте статьи (например, a, em, и т. Д.) , сами являются дочерними элементами body-text div. Что вы действительно хотите сделать, это выделить весь текст, найденный в определенном разделе div. Для удобства ваш исходный документ (связанный с комментариями выше) содержит узлы комментариев, которые отмечают начало и конец статьи . Они выглядят так:

<!-- inizio TESTO -->article text<!-- fine TESTO -->

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

Выбор текста после маркера начала

Следующее выражение выбирает нужные узлы:

//div[@class='body-text']/comment()[.=' inizio TESTO ']/following::text()

Проверка следующего урезанного документа:

<div class="body-text">
    <div class="fb-like-button" id="fb-like-head"></div>
    <h2><!-- inizio OCCHIELLO -->IRAN<!-- fine OCCHIELLO --></h2>
    <h1><!-- title -->"A Isfahan colpito sito nucleare"<br/>Londra annuncia azioni dure<!-- fine TITOLO --></h1>
    <h3><!-- summary -->Secondo il<em>Times</em>, fonti di intelligence...<br/><strong><br/></strong><!-- fine SOMMARIO --></h3>
    <div class="sidebar">Sidebar text...</div>
    <!-- inizio TESTO --><strong>TEHERAN</strong> - L'esplosione avvenuta 
    <a href="http://www.repubblica.it" class="footnote">lunedì scorso in Iran a Isfahan <sup>1</sup></a> avrebbe colpito un 
    sito nucleare. Lo hanno riferito fonti dell'intelligence israeliana al quotidiano britannico <em>The Times</em>, secondo le 
    quali alcune immagini satellitari "mostrano chiaramente colonne di fumo e la distruzione" di una struttura nucleare di Isfahan. 
    Sale, intanto, la tensione con la Gran Bretagna: dopo <a href="http://www.repubblica.it" class="footnote">l'assalto all'
    ambasciata britannica <sup>2</sup></a> ieri...<!-- fine TESTO -->
</div>

Возвращает следующие текстовые узлы:

[#text: TEHERAN]
[#text:  - L'esplosione avvenuta 
    ]
[#text: lunedì scorso in Iran a Isfahan ]
[#text: 1]
[#text:  avrebbe colpito un 
    sito nucleare. Lo hanno riferito fonti dell'intelligence israeliana al quotidiano britannico ]
[#text: The Times]
[#text: , secondo le 
    quali alcune immagini satellitari "mostrano chiaramente colonne di fumo e la distruzione" di una struttura nucleare di Isfahan. 
    Sale, intanto, la tensione con la Gran Bretagna: dopo ]
[#text: l'assalto all'
    ambasciata britannica ]
[#text: 2]
[#text:  ieri...]
[#text: 
]

Это набор узлов, который вы можете повторять и т. Д. Я не знаю R, поэтому не могу предоставить эти детали.

Выбор текста между начальным и конечным маркерами

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

//div[@class='body-text']//text()[preceding::comment()[.=' inizio TESTO '] and
                                  following::comment()[.=' fine TESTO ']]

Выбор текста между начальным и конечным маркерами (формула Кейсиана)

Обратите внимание, что предыдущее выражение может быть выражено более непосредственно как пересечение двух наборов узлов: 1) всех текстовых узлов после маркера начала и; 2) весь текстовый узел перед конечным маркером. В XPath 1.0 есть общая формула для пересечения:

$set1[count(.|$set2)=count($set2)]

Общая идея здесь, на английском языке, заключается в том, что если вы добавляете элемент из $set1 в $set2 и размер $set2 не изменяется, то этот узел уже должен быть в $set2. Множество всех узлов из $set1, для которых это имеет место, является пересечением $set1 и $set2.

В вашем конкретном случае:

$set1 = //div[@class='body-text']/comment()[.=' inizio TESTO ']/following::text()
$set2 = //div[@class='body-text']/comment()[.=' fine TESTO ']/preceding::text()

Собираем все вместе:

//div[@class='body-text']/comment()[.=' inizio TESTO ']/following::text()[
   count(.|//div[@class='body-text']/comment()[.=' fine TESTO ']/preceding::text())
     =
   count(//div[@class='body-text']/comment()[.=' fine TESTO ']/preceding::text())]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...