用 rvest 抓取 - 当标签不存在时用 NA 完成
Scraping with rvest - complete with NAs when tag is not present
我想解析这个 HTML: 并从中获取这个元素:
a) 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()
这可能不是最惯用的方法,但您可以像这样在 .product_price
节点上使用 lapply:
r.precio.antes <- page_source %>% html_nodes(".product_price") %>%
lapply(. %>% html_nodes(".normal_encontrado") %>% html_text() %>%
ifelse(identical(., character(0)), NA, .)) %>% unlist
每当找不到 .normal_encontrado
元素时,这将 return NA。
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)
相反,我对 lapply
使用 magrittr
语法,参见例如end of the Functional sequences paragraph here.
最后一个障碍是,如果未找到该元素,这将 return character(0)
而不是您想要的 NA
。所以我将 ifelse(identical(., character(0)), NA, .))
添加到 lapply 内的管道来修复它。
从您的目标向上一级,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
我不知道你是想修剪琴弦还是做其他任何事情,但这些阴谋很简单。
使用 XML 包解析带有 xmlTreeParse
的输入,然后使用 xpathSApply
对 product_price
class 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
如果没有找到标签,则返回returns一个字符(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 页面。
我想解析这个 HTML: 并从中获取这个元素:
a) 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()
这可能不是最惯用的方法,但您可以像这样在 .product_price
节点上使用 lapply:
r.precio.antes <- page_source %>% html_nodes(".product_price") %>%
lapply(. %>% html_nodes(".normal_encontrado") %>% html_text() %>%
ifelse(identical(., character(0)), NA, .)) %>% unlist
每当找不到 .normal_encontrado
元素时,这将 return NA。
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)
相反,我对 lapply
使用 magrittr
语法,参见例如end of the Functional sequences paragraph here.
最后一个障碍是,如果未找到该元素,这将 return character(0)
而不是您想要的 NA
。所以我将 ifelse(identical(., character(0)), NA, .))
添加到 lapply 内的管道来修复它。
从您的目标向上一级,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
我不知道你是想修剪琴弦还是做其他任何事情,但这些阴谋很简单。
使用 XML 包解析带有 xmlTreeParse
的输入,然后使用 xpathSApply
对 product_price
class 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
如果没有找到标签,则返回returns一个字符(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 页面。