抓取产品价格页面 returns 所有商品价格相同
Scraping page for product's price returns same price for all items
我正在为教育目标抓取网页。
我得到这些值:producto
(产品)、precio_antes
(price_before)、precio_actual
(price_now)和marca
(品牌)。
我得到了正确的产品但是:
precio_antes
return S/1,所有商品399.00,当价格不同时
precio_actual
return所有项目均不适用。
marca
returns "lg" 所有项目。
预期输出:
| ecommerce | marca | producto | precio_antes | precio_actual | |
|-----------|-------|------------------------------------------|--------------|---------------|---|
| wong | lg | LG Smart TV 49" Full HD 49LK5400 | S/1,399.00 | S/1,299.00 | |
| wong | lg | LG Smart TV 60" 4K UHD 60UK6200 ThinQ AI | S/2,599.00 | S/2,299.00 | |
当前输出
| ecommerce | marca | producto | precio_antes | precio_actual | |
|-----------|-------|------------------------------------------|--------------|---------------|---|
| wong | lg | LG Smart TV 49" Full HD 49LK5400 | S/1,399.00 | NA | |
| wong | lg | LG Smart TV 60" 4K UHD 60UK6200 ThinQ AI | S/1,399.00 | NA | |
我正在使用 RSelenium
,我认为我的 CSS 选择器技能需要提高。
library(RSelenium)
library(rvest)
library(dplyr)
library(stringr)
#start RSelenium
rD <- rsDriver(port = 4570L, browser = "chrome", version = "latest", chromever = "75.0.3770.90",
geckover = "latest", iedrver = NULL, phantomver = "2.1.1",
verbose = TRUE, check = TRUE)
remDr <- rD[["client"]]
#navigate to your page
remDr$navigate("https://www.wong.pe/tecnologia/televisores/tv")
#scroll down 10 times, waiting for the page to load at each time
for(i in 1:10){
remDr$executeScript(paste("scroll(0,",i*10000,");"))
Sys.sleep(3)
}
#get the page html
page_source<-remDr$getPageSource()
product_info <- function(node){
precio_antes <- html_nodes(node, 'span.product-prices__value') %>% html_text
precio_actual <- html_nodes(node, 'span.product-prices__value product-prices__value--best-price') %>% html_text
marca <- html_nodes(node,"p.brand") %>% html_text
producto <- html_nodes(node,"a.product-item__name") %>% html_text
precio_antes <- gsub("\S\/\. ", "", precio_antes)
precio_actual <- gsub("\S\/\. ", "", precio_actual)
data.frame(
ecommerce = "wong",
marca = ifelse(length(marca)==0, NA, marca),
producto = producto,
precio_antes = ifelse(length(precio_antes)==0, NA, precio_antes),
precio_actual = ifelse(length(precio_actual)==0, NA, precio_actual),
stringsAsFactors=F
)
}
doc <- read_html(iconv(page_source[[1]]), to="UTF-8") %>%
html_nodes("div.category-shelf-wrapper")
wong_tvs <- lapply(doc, product_info) %>%
bind_rows()
奖金:
即使我正在使用:
,我得到的西班牙文字符也不正确
LG Control Remoto Mágico AN-MR18BA #Should be Mágico
doc <- read_html(iconv(page_source[[1]]), to="UTF-8") %>%
html_nodes("div.category-shelf-wrapper")
为什么?
编辑增加了一个很好的规范,谢谢!
我假设您想在输出中使用 NA
再次跟踪丢失的元素。
按照这个假设,我会像另一个问题一样,再次寻找父元素。
可以找到父元素,例如通过 xpath:/html/body/div/div/div/div/div/div/div/div/ul/li/div/div[@class = 'product-item__bottom']
.
之后,您只需按照所需的格式拆分结果即可。
可重现的例子:
library(RSelenium)
rD <- rsDriver()
remDr <- rD$client
url = "https://www.wong.pe/tecnologia/televisores"
remDr$navigate(url)
productElems = remDr$findElements(
using = "xpath",
value = "/html/body/div/div/div/div/div/div/div/div/ul/li/div/div[@class = 'product-item__bottom']"
)
productInfoRaw = sapply(
X = productElems,
FUN = function(elem) elem$getElementText()
)
splittedRaw = sapply(productInfoRaw, strsplit, split = "\n")
splitted = lapply(splittedRaw, function(split){
if(length(split) == 5 & "Online" %in% split){
split[7] = split[4]
split[4] = NA
}
return(split)
})
infos = data.frame(
ecommerce = "wong",
marca = sapply(splitted, "[", 2),
producto = sapply(splitted, "[", 1),
precio_antes = sapply(splitted, "[", 4),
precio_actual = sapply(splitted, "[", 7)
)
head(infos)
输出:
> head(infos)
ecommerce marca producto precio_antes precio_actual
1 wong LG LG Smart TV 49" Full HD 49LK5400 S/1,399.00 S/1,299.00
2 wong LG LG Smart TV 60" 4K UHD 60UK6200 ThinQ AI S/2,599.00 S/2,299.00
3 wong LG LG Control Remoto Mágico AN-MR18BA <NA> S/199.00
4 wong AOC AOC Smart TV 32'' HD LE32S5970S Linux S/799.00 S/599.00
5 wong LG LG Smart TV 43" FHD 43LK5400 S/1,199.00 S/999.00
6 wong HISENSE Hisense Televisor LED 32'' HD H3218H4IP S/1,299.00 S/499.00
Selenium 很慢,只能作为最后的手段使用。在这种情况下,这是不必要的,因为目录 API 已公开。 API 还提供更丰富的 well-structured 数据。一次可以请求50条信息,所以可以通过0、50等递增,直到返回内容的总长度<50,然后就可以通过名称and/or整数位置拉取需要的信息了。
URL中的数字1000144和1000098指的是部门和类别,可以从https://www.wong.pe/tecnologia/televisores/tv的HTML中的一个script
节点中提取出来。我在这里没有这样做是为了让事情变得简单,但如果你想要一个更具适应性的刮板,这是可能的。
您也可以使用 paste0
而不是 glue
。您可以使用 lapply
代替 map_df
,然后使用 do.call
和 rbind
绑定行。您可以将 cbind
与 as.data.frame
一起使用,而不是 bind_cols
。我喜欢这些函数,因为它们简化了事情,避免了类型强制问题,并且通常提高了我的代码的可读性,但没有什么可以阻止您使用基本 R 函数。
为了简单起见,我保留了原始变量名。您可以使用 names(tvs_df) <- …
或在调用 map_df
之后使用 set_names(…)
更改它们,即 map_df(…) %>% set_names(…)
:
library(httr) # for `GET`
library(glue) # for `glue`, which allows cleaner syntax than `paste0`
library(purrr) # for `map_df` to map over list and return as dataframe
library(dplyr) # for `bind_cols`
i <- 0
cont_list <- list()
# Send requests and append data `cont_list` until fewer than 50 items returned.
repeat {
url <- glue("https://www.wong.pe/api/catalog_system/pub/products/search/",
"?&fq=C:/1000144/1000098/&_from={i}&_to={i + 49}")
cont <- content(GET(url))
cont_list <- c(cont_list, cont)
if (length(cont) < 50) break
i <- i + 50
}
# Names of desired data.
datl <- list(l1 = c("brand", "productName"),
l2 = c("Price", "ListPrice", "AvailableQuantity"))
# Extract data
tvs_df <- map_df(cont_list,
~ bind_cols(source = "wong.pe", .[datl$l1],
.$items[[1]]$sellers[[1]]$commertialOffer[datl$l2]))
哪个returns:
# A tibble: 54 x 6
source brand productName Price ListPrice AvailableQuantity
<chr> <chr> <chr> <dbl> <dbl> <int>
1 wong.pe LG "LG Smart TV 49\" Full HD 49LK5400" 1299 1399 276
2 wong.pe LG "LG Smart TV 60\" 4K UHD 60UK6200 ThinQ AI" 2299 2599 18
3 wong.pe LG LG Control Remoto Mágico AN-MR18BA 199 199 37
4 wong.pe AOC AOC Smart TV 32'' HD LE32S5970S Linux 599 799 90
5 wong.pe LG "LG Smart TV 43\" FHD 43LK5400" 999 1199 303
6 wong.pe Hisense Hisense Televisor LED 32'' HD H3218H4IP 499 1299 22
7 wong.pe LG "LG Smart TV 55\" 4K UHD 55UK6200 ThinQ AI" 1799 2199 31
8 wong.pe Panasonic Panasonic Smart TV Viera 32'' HD 32FS500 799 999 4
9 wong.pe AOC AOC Smart TV 55'' 4K UHD 55U7970 Linux 1299 2499 3
10 wong.pe AOC AOC Televisor LED 32'' HD 32M1370 499 699 4
# … with 44 more rows
我正在为教育目标抓取网页。
我得到这些值:producto
(产品)、precio_antes
(price_before)、precio_actual
(price_now)和marca
(品牌)。
我得到了正确的产品但是:
precio_antes
return S/1,所有商品399.00,当价格不同时precio_actual
return所有项目均不适用。marca
returns "lg" 所有项目。
预期输出:
| ecommerce | marca | producto | precio_antes | precio_actual | |
|-----------|-------|------------------------------------------|--------------|---------------|---|
| wong | lg | LG Smart TV 49" Full HD 49LK5400 | S/1,399.00 | S/1,299.00 | |
| wong | lg | LG Smart TV 60" 4K UHD 60UK6200 ThinQ AI | S/2,599.00 | S/2,299.00 | |
当前输出
| ecommerce | marca | producto | precio_antes | precio_actual | |
|-----------|-------|------------------------------------------|--------------|---------------|---|
| wong | lg | LG Smart TV 49" Full HD 49LK5400 | S/1,399.00 | NA | |
| wong | lg | LG Smart TV 60" 4K UHD 60UK6200 ThinQ AI | S/1,399.00 | NA | |
我正在使用 RSelenium
,我认为我的 CSS 选择器技能需要提高。
library(RSelenium)
library(rvest)
library(dplyr)
library(stringr)
#start RSelenium
rD <- rsDriver(port = 4570L, browser = "chrome", version = "latest", chromever = "75.0.3770.90",
geckover = "latest", iedrver = NULL, phantomver = "2.1.1",
verbose = TRUE, check = TRUE)
remDr <- rD[["client"]]
#navigate to your page
remDr$navigate("https://www.wong.pe/tecnologia/televisores/tv")
#scroll down 10 times, waiting for the page to load at each time
for(i in 1:10){
remDr$executeScript(paste("scroll(0,",i*10000,");"))
Sys.sleep(3)
}
#get the page html
page_source<-remDr$getPageSource()
product_info <- function(node){
precio_antes <- html_nodes(node, 'span.product-prices__value') %>% html_text
precio_actual <- html_nodes(node, 'span.product-prices__value product-prices__value--best-price') %>% html_text
marca <- html_nodes(node,"p.brand") %>% html_text
producto <- html_nodes(node,"a.product-item__name") %>% html_text
precio_antes <- gsub("\S\/\. ", "", precio_antes)
precio_actual <- gsub("\S\/\. ", "", precio_actual)
data.frame(
ecommerce = "wong",
marca = ifelse(length(marca)==0, NA, marca),
producto = producto,
precio_antes = ifelse(length(precio_antes)==0, NA, precio_antes),
precio_actual = ifelse(length(precio_actual)==0, NA, precio_actual),
stringsAsFactors=F
)
}
doc <- read_html(iconv(page_source[[1]]), to="UTF-8") %>%
html_nodes("div.category-shelf-wrapper")
wong_tvs <- lapply(doc, product_info) %>%
bind_rows()
奖金:
即使我正在使用:
,我得到的西班牙文字符也不正确LG Control Remoto Mágico AN-MR18BA #Should be Mágico
doc <- read_html(iconv(page_source[[1]]), to="UTF-8") %>%
html_nodes("div.category-shelf-wrapper")
为什么?
编辑增加了一个很好的规范,谢谢!
我假设您想在输出中使用 NA
再次跟踪丢失的元素。
按照这个假设,我会像另一个问题一样,再次寻找父元素。
可以找到父元素,例如通过 xpath:/html/body/div/div/div/div/div/div/div/div/ul/li/div/div[@class = 'product-item__bottom']
.
之后,您只需按照所需的格式拆分结果即可。
可重现的例子:
library(RSelenium)
rD <- rsDriver()
remDr <- rD$client
url = "https://www.wong.pe/tecnologia/televisores"
remDr$navigate(url)
productElems = remDr$findElements(
using = "xpath",
value = "/html/body/div/div/div/div/div/div/div/div/ul/li/div/div[@class = 'product-item__bottom']"
)
productInfoRaw = sapply(
X = productElems,
FUN = function(elem) elem$getElementText()
)
splittedRaw = sapply(productInfoRaw, strsplit, split = "\n")
splitted = lapply(splittedRaw, function(split){
if(length(split) == 5 & "Online" %in% split){
split[7] = split[4]
split[4] = NA
}
return(split)
})
infos = data.frame(
ecommerce = "wong",
marca = sapply(splitted, "[", 2),
producto = sapply(splitted, "[", 1),
precio_antes = sapply(splitted, "[", 4),
precio_actual = sapply(splitted, "[", 7)
)
head(infos)
输出:
> head(infos)
ecommerce marca producto precio_antes precio_actual
1 wong LG LG Smart TV 49" Full HD 49LK5400 S/1,399.00 S/1,299.00
2 wong LG LG Smart TV 60" 4K UHD 60UK6200 ThinQ AI S/2,599.00 S/2,299.00
3 wong LG LG Control Remoto Mágico AN-MR18BA <NA> S/199.00
4 wong AOC AOC Smart TV 32'' HD LE32S5970S Linux S/799.00 S/599.00
5 wong LG LG Smart TV 43" FHD 43LK5400 S/1,199.00 S/999.00
6 wong HISENSE Hisense Televisor LED 32'' HD H3218H4IP S/1,299.00 S/499.00
Selenium 很慢,只能作为最后的手段使用。在这种情况下,这是不必要的,因为目录 API 已公开。 API 还提供更丰富的 well-structured 数据。一次可以请求50条信息,所以可以通过0、50等递增,直到返回内容的总长度<50,然后就可以通过名称and/or整数位置拉取需要的信息了。
URL中的数字1000144和1000098指的是部门和类别,可以从https://www.wong.pe/tecnologia/televisores/tv的HTML中的一个script
节点中提取出来。我在这里没有这样做是为了让事情变得简单,但如果你想要一个更具适应性的刮板,这是可能的。
您也可以使用 paste0
而不是 glue
。您可以使用 lapply
代替 map_df
,然后使用 do.call
和 rbind
绑定行。您可以将 cbind
与 as.data.frame
一起使用,而不是 bind_cols
。我喜欢这些函数,因为它们简化了事情,避免了类型强制问题,并且通常提高了我的代码的可读性,但没有什么可以阻止您使用基本 R 函数。
为了简单起见,我保留了原始变量名。您可以使用 names(tvs_df) <- …
或在调用 map_df
之后使用 set_names(…)
更改它们,即 map_df(…) %>% set_names(…)
:
library(httr) # for `GET`
library(glue) # for `glue`, which allows cleaner syntax than `paste0`
library(purrr) # for `map_df` to map over list and return as dataframe
library(dplyr) # for `bind_cols`
i <- 0
cont_list <- list()
# Send requests and append data `cont_list` until fewer than 50 items returned.
repeat {
url <- glue("https://www.wong.pe/api/catalog_system/pub/products/search/",
"?&fq=C:/1000144/1000098/&_from={i}&_to={i + 49}")
cont <- content(GET(url))
cont_list <- c(cont_list, cont)
if (length(cont) < 50) break
i <- i + 50
}
# Names of desired data.
datl <- list(l1 = c("brand", "productName"),
l2 = c("Price", "ListPrice", "AvailableQuantity"))
# Extract data
tvs_df <- map_df(cont_list,
~ bind_cols(source = "wong.pe", .[datl$l1],
.$items[[1]]$sellers[[1]]$commertialOffer[datl$l2]))
哪个returns:
# A tibble: 54 x 6
source brand productName Price ListPrice AvailableQuantity
<chr> <chr> <chr> <dbl> <dbl> <int>
1 wong.pe LG "LG Smart TV 49\" Full HD 49LK5400" 1299 1399 276
2 wong.pe LG "LG Smart TV 60\" 4K UHD 60UK6200 ThinQ AI" 2299 2599 18
3 wong.pe LG LG Control Remoto Mágico AN-MR18BA 199 199 37
4 wong.pe AOC AOC Smart TV 32'' HD LE32S5970S Linux 599 799 90
5 wong.pe LG "LG Smart TV 43\" FHD 43LK5400" 999 1199 303
6 wong.pe Hisense Hisense Televisor LED 32'' HD H3218H4IP 499 1299 22
7 wong.pe LG "LG Smart TV 55\" 4K UHD 55UK6200 ThinQ AI" 1799 2199 31
8 wong.pe Panasonic Panasonic Smart TV Viera 32'' HD 32FS500 799 999 4
9 wong.pe AOC AOC Smart TV 55'' 4K UHD 55U7970 Linux 1299 2499 3
10 wong.pe AOC AOC Televisor LED 32'' HD 32M1370 499 699 4
# … with 44 more rows