【问题标题】:Improving my R code - advice wanted on better way of coding? [closed]改进我的 R 代码 - 关于更好的编码方式的建议? [关闭]
【发布时间】:2016-09-25 10:47:00
【问题描述】:

我的代码正在运行,它是一个网页抓取脚本,首先从网页的 URL 中获取,然后使用 for 循环遍历所有 URL。在循环期间,它需要一些信息并将其保存到一个数据帧中,我首先在循环之前将其创建为一个空数据帧。该进程使用 rbind 并且工作正常。

但是,我觉得这段代码不是最优的,可能有一个包,我认为解决方案将是 lapply ......也许不是。但我希望有人能给我指点一个更好的编码方法(如果存在的话)以及如何实现它。

library(rvest)

URL <- "http://www.transfermarkt.com/premier-league/startseite/wettbewerb/GB1"

WS <- read_html(URL)

URLs <- WS %>% html_nodes(".hide-for-pad .vereinprofil_tooltip") %>% html_attr("href") %>% as.character()
URLs <- paste0("http://www.transfermarkt.com",URLs)

Catcher1 <- data.frame(Player=character(),P_URL=character())

for (i in URLs) {
  
  WS1 <- read_html(i)
  Player <- WS1 %>% html_nodes("#yw1 .spielprofil_tooltip") %>% html_text() %>% as.character()
  P_URL <- WS1 %>% html_nodes("#yw1 .spielprofil_tooltip") %>% html_attr("href") %>% as.character()
  temp <- data.frame(Player,P_URL)
  Catcher1 <- rbind(Catcher1,temp)
  cat("*")
}

【问题讨论】:

  • 我投票结束这个问题,因为它应该被移到代码审查 stackexchange

标签: r


【解决方案1】:

您可以尝试使用purrr 代替循环,如下所示:

require(rvest)
require(purrr)
require(tibble)

URLs %>% 
  map(read_html) %>% 
  map(html_nodes, "#yw1 .spielprofil_tooltip") %>% 
  map_df(~tibble(Player = html_text(.), P_URL = html_attr(., "href")))

时间:

   user  system elapsed 
  2.939   2.746   5.699 

耗时最多的一步是通过map(read_html)爬取。
要使您瘫痪,您可以使用例如plyr的并行后端如下:

require(httr)
doMC::registerDoMC(cores=3) # cores depending on your system
plyr::llply(URLs, GET, .parallel = TRUE) %>% 
  map(read_html) %>% 
  map(html_nodes, "#yw1 .spielprofil_tooltip") %>% 
  map_df(~tibble(Player = html_text(.), P_URL = html_attr(., "href")))

不知何故,我的 Rstudio 在使用 plyr::llply(URLs, read_html, .parallel = TRUE) 时崩溃了,这就是为什么我使用底层 httr::GET 并在下一步中通过 map(read_html) 解析结果。所以抓取是并行完成的,但响应的解析是顺序完成的。

时间:

   user  system elapsed 
  2.505   0.337   2.940 

在这两种情况下,结果如下所示:

# A tibble: 1,036 × 2
          Player                                P_URL
           <chr>                                <chr>
1   David de Gea   /david-de-gea/profil/spieler/59377
2      D. de Gea   /david-de-gea/profil/spieler/59377
3  Sergio Romero  /sergio-romero/profil/spieler/30690
4      S. Romero  /sergio-romero/profil/spieler/30690
5  Sam Johnstone /sam-johnstone/profil/spieler/110864
6   S. Johnstone /sam-johnstone/profil/spieler/110864
7    Daley Blind    /daley-blind/profil/spieler/12282
8       D. Blind    /daley-blind/profil/spieler/12282
9    Eric Bailly   /eric-bailly/profil/spieler/286384
10     E. Bailly   /eric-bailly/profil/spieler/286384
# ... with 1,026 more rows

【讨论】:

  • 感谢 Floo0,一些很棒的想法。如果抓取的数据的 CSS 标识符“#yw1 .spielprofil_tooltip”不同,那会改变您的选择吗?
  • 在您的示例中,所有信息都基于单个 CSS 标识符。这就是为什么它如此容易。如果涉及更多标识符,我很可能会使用一个单独的函数来接收文档并返回我想要的数据框。例如。在您的代码中以WS1 作为输入并返回temp。
【解决方案2】:

您的主要问题是您正在增长一个对象。在这种情况下,您正在增长一个数据框。为了解决这个问题,在循环之前创建一个大数据框,并填充它。这是否是瓶颈,很难说。如果length(URLs) 很小,那么它不会有太大的不同。

另一个可能的加速是并行运行循环。也许使用parallel::parSapply。要将循环转换为并行版本,只需将“循环”部分移动到函数中,您的代码将变为:

parallel::parSapply(1:URLs, get_resource)

您也可以尝试foreach 包。

【讨论】:

  • 谢谢,一直在研究 foreach 并取得了一些进展
猜你喜欢
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
  • 2019-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多