2015-10-21 2 views
4

Я хочу, чтобы разобрать этот HTML: и получить эти элементы из него:Зачистка с rvest - полный НСБОМ, когда тег нет

а) p тега, с class: "normal_encontrado".
b) div с class: "price".

Иногда в некоторых продуктах нет метки p. Если это так, то к вектору, который собирает текст с этих узлов, следует добавить NA.

Идея состоит в том, чтобы иметь 2 вектора с одинаковой длиной, и после их объединения сделать data.frame. Есть идеи?

HTML-часть:

<html> 
<head></head> 
<body> 

<div class="product_price" id="product_price_186251"> 
    <p class="normal_encontrado"> 
    S/. 2,799.00 
    </p> 

    <div id="WC_CatalogEntryDBThumbnailDisplayJSPF_10461_div_10" class="price"> 
    S/. 2,299.00 
    </div>  
</div> 

<div class="product_price" id="product_price_232046"> 
    <div id="WC_CatalogEntryDBThumbnailDisplayJSPF_10461_div_10" class="price"> 
    S/. 4,999.00 
    </div> 
</div> 
</body> 
</html> 

R Код:

library(rvest) 

page_source <- read_html("r.html") 

r.precio.antes <- page_source %>% 
html_nodes(".normal_encontrado") %>% 
html_text() 

r.precio.actual <- page_source %>% 
html_nodes(".price") %>% 
html_text() 
+1

Что-то вроде этого может быть полезно - [R dataframe из xml, когда значения несколько или отсутствуют] (http://stackoverflow.com/questions/17349630/r-dataframe-from-xml-when-values-are-multiple -or-missing) – thelatemail

ответ

3

Если тег не найден, rvest возвращает символ (0). Так если вы найдете самое большее один текущий и один обычная цена в каждом div.product_price, вы можете использовать это:

pacman::p_load("rvest", "dplyr") 

get_prices <- function(node){ 
    r.precio.antes <- html_nodes(node, 'p.normal_encontrado') %>% html_text 
    r.precio.actual <- html_nodes(node, 'div.price') %>% html_text 

    data.frame(
    precio.antes = ifelse(length(r.precio.antes)==0, NA, r.precio.antes), 
    precio.actual = ifelse(length(r.precio.actual)==0, NA, r.precio.actual), 
    stringsAsFactors=F 
) 

} 

doc <- read_html('test.html') %>% html_nodes("div.product_price") 
lapply(doc, get_prices) %>% 
    rbind_all 

Отредактировано: я понял входные данные, так изменили сценарий, чтобы работать только с единственная страница html.

+0

Более понятный способ, спасибо. Мне также нравится метод Grothendick, но я никогда не использовал XML-пакет. –

0

Это не может быть наиболее идиоматических способ сделать это, но вы можете использовать lapply над .product_price узлов, как это:

r.precio.antes <- page_source %>% html_nodes(".product_price") %>% 
    lapply(. %>% html_nodes(".normal_encontrado") %>% html_text() %>% 
    ifelse(identical(., character(0)), NA, .)) %>% unlist 

Это вернет NA всякий раз, когда элемент .normal_encontrado не найден.

r.precio.antes 
# [1] "\n     S/. 2,799.00\n    " 
# [2] NA 

length(r.precio.antes) # 2 

Если бы я хотел, чтобы разработать код, чтобы сделать его более четким, сначала я изолировать .product_price узлы:

product_nodes <- page_source %>% html_nodes(".product_price") 

Тогда я мог бы использовать lapply более традиционным способом:

r.precio.antes <- lapply(product_nodes, function(pn) { 
    pn %>% html_nodes(".normal_encontrado") %>% html_text() 
}) 
r.precio.antes <- unlist(r.precio.antes) 

Вместо этого я использую синтаксис magrittr для lapply, см., Например, end of the Functional sequences paragraph here.

Последним препятствием является то, что если элемент не найден, это вернет character(0), а не NA, как вы хотели. Поэтому я добавляю ifelse(identical(., character(0)), NA, .)) к трубе внутри лапши, чтобы исправить это.

+0

Не могли бы вы объяснить код? Особенно эта часть: 'lapply (.%>% Html_nodes (". Normal_encontrado ")' why is "." Там (после lapply)? И также: '(function (x) ifelse (идентичный (x, character (0)), NA, x))). Благодарю. –

+0

На самом деле, я понял, что вы можете просто сделать ifelse (идентичный (., Character (0)), NA,.)) 'Вместо синтаксиса' (function (x) ...) '. Я разработал код и объяснения. Яснее? – cocquemas

2

Go один уровень вверх от вашей цели и lapply над каждым родительским элементом:

library(xml2) 
library(rvest) 

pg <- read_html('<html> 
<head></head> 
<body> 

<div class="product_price" id="product_price_186251"> 
    <p class="normal_encontrado"> 
    S/. 2,799.00 
    </p> 

    <div id="WC_CatalogEntryDBThumbnailDisplayJSPF_10461_div_10" class="price"> 
    S/. 2,299.00 
    </div>  
</div> 

<div class="product_price" id="product_price_232046"> 
    <div id="WC_CatalogEntryDBThumbnailDisplayJSPF_10461_div_10" class="price"> 
    S/. 4,999.00 
    </div> 
</div> 
</body> 
</html>') 

prod <- html_nodes(pg, "div.product_price") 
do.call(rbind, lapply(prod, function(x) { 
    norm <- tryCatch(xml_text(xml_node(x, "p.normal_encontrado")), 
        error=function(err) {NA}) 
    price <- tryCatch(xml_text(xml_node(x, "div.price")), 
        error=function(err) {NA}) 
    data.frame(norm, price, stringsAsFactors=FALSE) 
})) 

##      norm     price 
## 1 \n S/. 2,799.00\n \n S/. 2,299.00\n 
## 2     <NA> \n S/. 4,999.00\n 

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

4

Использование пакета XML разбора ввод с xmlTreeParse, а затем использовать xpathSApply для interate над product_price класса div узлов. Для каждого такого узла анонимная функция получает значения подносов div и p. Полученная матрица символов m перерабатывается в кадр данных DF, а столбцы очищаются, удаляя любой символ, который не является точкой или цифрой, а также удаляет любую точку, за которой следует незначащая. Копировать результат в числовой. Обратите внимание, что никакой специальной обработки для отсутствующего случая p не требуется.

# input 

Lines <- '<html> 
<head></head> 
<body> 

<div class="product_price" id="product_price_186251"> 
    <p class="normal_encontrado"> 
    S/. 2,799.00 
    </p> 

    <div id="WC_CatalogEntryDBThumbnailDisplayJSPF_10461_div_10" class="price"> 
    S/. 2,299.00 
    </div>  
</div> 

<div class="product_price" id="product_price_232046"> 
    <div id="WC_CatalogEntryDBThumbnailDisplayJSPF_10461_div_10" class="price"> 
    S/. 4,999.00 
    </div> 
</div> 
</body> 
</html>' 

# code to read input and produce a data.frame 

library(XML) 
doc <- xmlTreeParse(Lines, asText = TRUE, useInternalNodes = TRUE) 

m <- xpathSApply(doc, "//div[@class = 'product_price']", function(node) { 
    list(p = xmlValue(node[["p"]]), div = xmlValue(node[["div"]])) }) 

DF <- as.data.frame(t(m), stringsAsFactors = FALSE) # rework into data frame 
DF[] <- lapply(DF, function(x) as.numeric(gsub("[^.0-9]|[.]\\D", "", x))) # clean 

Результат:

> DF 
    p div 
1 2799 2299 
2 NA 4999 
Смежные вопросы