by_row vs 逐行迭代
by_row vs rowwise iteration
有人知道by_row
和rowwise
有什么区别吗?我正在尝试抓取 3 个简单的网站,但我似乎无法使用任何一种方法,所以我不确定我是否只是在使用 purr
/dplyr
错误。
数据:
structure(list(beer_brewerid = c("8481", "3228", "10325"), link =
c("https://www.ratebeer.com/beer/8481/", "https://www.ratebeer.com/beer/3228/", "https://www.ratebeer.com/beer/10325/" ), scrapedname = c("", "", "")), .Names = c("beer_brewerid", "link", "scrapedname"), row.names = c(NA, 3L), class = "data.frame")
对于每个 URL(或行),我想使用以下函数抓取网页:
dplyr approach:
table %>%
rowwise() %>%
read_html() %>%
extract2(2) %>%
html_nodes("#_brand4 span") %>%
html_text()
呼噜声接近:
#Apply function to each row
table %>%
by_row(..f = parserows(), collate = c("rows"), .to = "scrapedname")
#Takes in row
parserows = function(){
read_html() %>%
extract2(., 2) %>%
html_nodes("#_brand4 span") %>%
html_text()
}
在 purr
方法中,我不断收到一个错误,其中 x 没有默认值。该值不应该来自行号吗?否则我会写一个 for 循环来指定行号所在的索引。
使用这个 magrittr 管道,我的 code.So:
一直出现超时错误
如何在使用 purr/dplyr 遍历我的 df 中的所有元素时避免超时错误?如果是这样,我应该考虑使用 trycatch
还是某种错误处理机制来捕获发生的错误?
rowwise/by_row真的适合这个任务吗?我认为这些函数旨在迭代一行中的每个元素,这并不是我要解决的手头问题。谢谢。
output = table$link %>%
extract() %>%
map(read_html) %>%
html_nodes(row,"#_brand4 span") %>%
html_text(row)
@Thomas K 的建议可能如下所示:
首先只有 purrr
:
library(purrr)
library(dplyr)
library(httr)
library(xml2)
library(rvest)
table$link %>%
purrr::set_names() %>%
map(read_html) %>%
map(html_node, "#_brand4 span") %>%
map(html_text)
# $`https://www.ratebeer.com/beer/8481/`
# [1] "Föroya Bjór"
#
# $`https://www.ratebeer.com/beer/3228/`
# [1] "King Brewing Company"
#
# $`https://www.ratebeer.com/beer/10325/`
# [1] "Bavik-De Brabandere"
(注意不需要使用 html_nodes
(复数),而不是 html_node
(单数)。
混合 dplyr
/purrr
替代方案,如果您需要重复使用它们,它可以让您将每个 html 文档保存在一个整洁的数据框中:
res <-
table %>%
mutate(html = map(link, read_html),
brand_node = map(html, html_node, "#_brand4 span"),
scrapedname = map_chr(brand_node, html_text))
html
和 brand_node
列存储为外部指针,打印不太友好,所以这里是没有它们的结果数据框:
select(res, - html, - brand_node)
# beer_brewerid link scrapedname
# 1 8481 https://www.ratebeer.com/beer/8481/ Föroya Bjór
# 2 3228 https://www.ratebeer.com/beer/3228/ King Brewing Company
# 3 10325 https://www.ratebeer.com/beer/10325/ Bavik-De Brabandere
glimpse(res)
# Observations: 3
# Variables: 5
# $ beer_brewerid <chr> "8481", "3228", "10325"
# $ link <chr> "https://www.ratebeer.com/beer/8481/", "https://www.ratebeer.com/beer/3228/", "https://www.ratebeer.com/beer/10325/"
# $ scrapedname <chr> "Föroya Bjór", "King Brewing Company", "Bavik-De Brabandere"
# $ html <list> [<html lang="en">, <html lang="en">, <html lang="en">]
# $ brand_node <list> [<span itemprop="name">, <span itemprop="name">, <span itemprop="name">]
对于超时问题,您也可以根据@Thomas K 的评论,简单地将 read_html
包装在 safely()
或 possibly()
中(这确实是 tryCatch
的替代方案) ):
safe_read_html <- possibly(read_html, otherwise = read_html("<html></html>"))
但是为了解决您在服务器上过于努力的(可能的)实际问题,我建议 httr::RETRY()
让您重试 "exponential backoff times":
safe_retry_read_html <- possibly(~ read_html(RETRY("GET", url = .x)), otherwise = read_html("<html></html>"))
抓取的一个好习惯是在服务器上真正温和,所以你甚至可以在每个请求之前手动添加一个偏移时间,例如 Sys.sleep(1 + runif(1))
。
table$link %>%
c("https://www.wrong-url.foobar") %>%
purrr::set_names() %>%
map(~ {
Sys.sleep(1 + runif(1))
safe_retry_read_html(.x)
}) %>%
map(html_node, "#_brand4 span") %>%
map_chr(html_text)
# https://www.ratebeer.com/beer/8481/ https://www.ratebeer.com/beer/3228/
# "Föroya Bjór" "King Brewing Company"
# https://www.ratebeer.com/beer/10325/ https://www.wrong-url.foobar
# "Bavik-De Brabandere" NA
最后,还有一个关于 by_row()
/rowwise()
的问题。
首先,请注意 by_row
已从 purrr
的开发版本中删除,并移至单独的包 purrrlyr
,无论如何它已被弃用,建议 "use a combination of: tidyr::nest()
; dplyr::mutate()
; purrr::map()
"
从 help("rowwise")
开始,rowwise
主要是 "used for the results of do()
when you create list-variables"。
所以,不,"really meant for this task",它们都是多余的。
有人知道by_row
和rowwise
有什么区别吗?我正在尝试抓取 3 个简单的网站,但我似乎无法使用任何一种方法,所以我不确定我是否只是在使用 purr
/dplyr
错误。
数据:
structure(list(beer_brewerid = c("8481", "3228", "10325"), link =
c("https://www.ratebeer.com/beer/8481/", "https://www.ratebeer.com/beer/3228/", "https://www.ratebeer.com/beer/10325/" ), scrapedname = c("", "", "")), .Names = c("beer_brewerid", "link", "scrapedname"), row.names = c(NA, 3L), class = "data.frame")
对于每个 URL(或行),我想使用以下函数抓取网页:
dplyr approach:
table %>%
rowwise() %>%
read_html() %>%
extract2(2) %>%
html_nodes("#_brand4 span") %>%
html_text()
呼噜声接近:
#Apply function to each row
table %>%
by_row(..f = parserows(), collate = c("rows"), .to = "scrapedname")
#Takes in row
parserows = function(){
read_html() %>%
extract2(., 2) %>%
html_nodes("#_brand4 span") %>%
html_text()
}
在 purr
方法中,我不断收到一个错误,其中 x 没有默认值。该值不应该来自行号吗?否则我会写一个 for 循环来指定行号所在的索引。
使用这个 magrittr 管道,我的 code.So:
一直出现超时错误如何在使用 purr/dplyr 遍历我的 df 中的所有元素时避免超时错误?如果是这样,我应该考虑使用
trycatch
还是某种错误处理机制来捕获发生的错误?rowwise/by_row真的适合这个任务吗?我认为这些函数旨在迭代一行中的每个元素,这并不是我要解决的手头问题。谢谢。
output = table$link %>% extract() %>% map(read_html) %>% html_nodes(row,"#_brand4 span") %>% html_text(row)
@Thomas K 的建议可能如下所示:
首先只有 purrr
:
library(purrr)
library(dplyr)
library(httr)
library(xml2)
library(rvest)
table$link %>%
purrr::set_names() %>%
map(read_html) %>%
map(html_node, "#_brand4 span") %>%
map(html_text)
# $`https://www.ratebeer.com/beer/8481/`
# [1] "Föroya Bjór"
#
# $`https://www.ratebeer.com/beer/3228/`
# [1] "King Brewing Company"
#
# $`https://www.ratebeer.com/beer/10325/`
# [1] "Bavik-De Brabandere"
(注意不需要使用 html_nodes
(复数),而不是 html_node
(单数)。
混合 dplyr
/purrr
替代方案,如果您需要重复使用它们,它可以让您将每个 html 文档保存在一个整洁的数据框中:
res <-
table %>%
mutate(html = map(link, read_html),
brand_node = map(html, html_node, "#_brand4 span"),
scrapedname = map_chr(brand_node, html_text))
html
和 brand_node
列存储为外部指针,打印不太友好,所以这里是没有它们的结果数据框:
select(res, - html, - brand_node)
# beer_brewerid link scrapedname
# 1 8481 https://www.ratebeer.com/beer/8481/ Föroya Bjór
# 2 3228 https://www.ratebeer.com/beer/3228/ King Brewing Company
# 3 10325 https://www.ratebeer.com/beer/10325/ Bavik-De Brabandere
glimpse(res)
# Observations: 3
# Variables: 5
# $ beer_brewerid <chr> "8481", "3228", "10325"
# $ link <chr> "https://www.ratebeer.com/beer/8481/", "https://www.ratebeer.com/beer/3228/", "https://www.ratebeer.com/beer/10325/"
# $ scrapedname <chr> "Föroya Bjór", "King Brewing Company", "Bavik-De Brabandere"
# $ html <list> [<html lang="en">, <html lang="en">, <html lang="en">]
# $ brand_node <list> [<span itemprop="name">, <span itemprop="name">, <span itemprop="name">]
对于超时问题,您也可以根据@Thomas K 的评论,简单地将 read_html
包装在 safely()
或 possibly()
中(这确实是 tryCatch
的替代方案) ):
safe_read_html <- possibly(read_html, otherwise = read_html("<html></html>"))
但是为了解决您在服务器上过于努力的(可能的)实际问题,我建议 httr::RETRY()
让您重试 "exponential backoff times":
safe_retry_read_html <- possibly(~ read_html(RETRY("GET", url = .x)), otherwise = read_html("<html></html>"))
抓取的一个好习惯是在服务器上真正温和,所以你甚至可以在每个请求之前手动添加一个偏移时间,例如 Sys.sleep(1 + runif(1))
。
table$link %>%
c("https://www.wrong-url.foobar") %>%
purrr::set_names() %>%
map(~ {
Sys.sleep(1 + runif(1))
safe_retry_read_html(.x)
}) %>%
map(html_node, "#_brand4 span") %>%
map_chr(html_text)
# https://www.ratebeer.com/beer/8481/ https://www.ratebeer.com/beer/3228/
# "Föroya Bjór" "King Brewing Company"
# https://www.ratebeer.com/beer/10325/ https://www.wrong-url.foobar
# "Bavik-De Brabandere" NA
最后,还有一个关于 by_row()
/rowwise()
的问题。
首先,请注意 by_row
已从 purrr
的开发版本中删除,并移至单独的包 purrrlyr
,无论如何它已被弃用,建议 "use a combination of: tidyr::nest()
; dplyr::mutate()
; purrr::map()
"
从 help("rowwise")
开始,rowwise
主要是 "used for the results of do()
when you create list-variables"。
所以,不,"really meant for this task",它们都是多余的。