有谁知道有什么区别之间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")
Run Code Online (Sandbox Code Playgroud)
对于每个URL(或行),我想使用以下函数来抓取网页:
dplyr approach:
table %>%
rowwise() %>%
read_html() %>%
extract2(2) %>%
html_nodes("#_brand4 span") %>%
html_text()
Run Code Online (Sandbox Code Playgroud)
Purr方法:
#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()
}
Run Code Online (Sandbox Code Playgroud)
在这种purr方法中,我一直收到一个错误,其中缺少x而没有默认值.这个值不应该来自行号吗?否则我会写一个for循环来指定行号所在的索引.
使用这个magrittr管道,我的代码不断出现超时错误.所以:
使用purr/dplyr迭代我的df中的所有元素时,如何避免超时错误?如果是这样,我应该考虑使用trycatch或某种错误处理机制来捕获错误吗?
rowwise/by_row真的意味着这项任务吗?我认为这些函数是针对一行中的每个元素进行迭代的,这不是我想要解决的问题.谢谢.
output = table$link %>%
extract() %>%
map(read_html) %>%
html_nodes(row,"#_brand4 span") %>%
html_text(row)
Run Code Online (Sandbox Code Playgroud)以下是@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"
Run Code Online (Sandbox Code Playgroud)
(注意,不需要使用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))
Run Code Online (Sandbox Code Playgroud)
该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">]
Run Code Online (Sandbox Code Playgroud)
对于超时问题,您也可以根据@Thomas K的评论简单地换read_html入safely()或possibly()(确实是替代tryCatch):
safe_read_html <- possibly(read_html, otherwise = read_html("<html></html>"))
Run Code Online (Sandbox Code Playgroud)
但是为了解决你在服务器上过于努力的(可能的)实际问题,我建议httr::RETRY()让你以"指数退避时间"重试:
safe_retry_read_html <- possibly(~ read_html(RETRY("GET", url = .x)), otherwise = read_html("<html></html>"))
Run Code Online (Sandbox Code Playgroud)
抓取时的一个好习惯是在服务器上实现温和,因此您甚至可以在每个请求之前手动添加偏移时间,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
Run Code Online (Sandbox Code Playgroud)
最后,有关于by_row()/ 的单独问题rowwise().
首先,需要注意的是by_row已经从开发版本中删除purrr,并移动到一个单独的包,purrrlyr其中它反正过时,它的建议"用的组合:tidyr::nest(); dplyr::mutate(); purrr::map()"
From help("rowwise"),rowwise主要用于"用于do()创建列表变量时的结果".
所以,不,不是"真的意味着这个任务",它们将是多余的.