【问题标题】:Web Scraping with Rvest, Lapply, Rbind, and Selector Gadget使用 Rvest、Lapply、Rbind 和 Selector Gadget 进行网页抓取
【发布时间】:2020-06-27 21:10:15
【问题描述】:

我是网络抓取的新手,在抓取网站内的多个页面时遇到困难。感谢社区的任何反馈!

目标:从 2019 年 cfbstats.com 抓取每个团队的统计数据,并将每个团队的数据放入一个数据框或自定义团队数据框。

操作: 在下面的代码中,我编写了一个 html 调用,将每个团队(小示例)的唯一标识符粘贴到 html 代码中。之后,我使用 lapply 函数和 rvest 来提取每个团队的数据。最后,使用 rbind 和 do.call 来加入所有的 pulls。

问题:在@Dave2e 的帮助下,我意识到 rbind 合并所有被抓取的数据表时遇到了问题。从网络抓取中加入数据表有什么建议吗? match.names(clabs, names(xi)) 中的错误: 名字与以前的名字不匹配

library(tidyverse)
library(rvest)

team_id <- c(721, 5, 8)
teams <- paste('http://cfbstats.com/2019/team/', team_id, '/index.html', sep = "")

df_team_stats <- lapply(teams, function(i){
  webpage <- read_html(i)
  team_table <- html_nodes(webpage, '.team-statistics')
  overall_stats <- html_table(team_table)[[1]]
})

finaldf <- do.call(rbind, df_team_stats) 

【问题讨论】:

  • 有 2 个不同的节点,“class= team-statistics”。您只希望 CSS 等于“table”和 class= team-statistics。因此正确的调用是:html_nodes(webpage, 'table.team-statistics')
  • 谢谢@Dave2e!这有助于我更接近解决方案。非常感激。插入您的建议后,我发现我的 rbind 存在一些问题。 'match.names(clabs, names(xi)) 中的错误:名称与以前的名称不匹配。
  • 使用 dplyr 包中的 bind_rows() 函数。它是do.call(rbind, ...) 的替代品,而且它具有列名匹配的好处。
  • @Dave2e 另一个很棒的电话。非常感谢您的所有帮助!

标签: r web-scraping lapply rvest


【解决方案1】:

这里有几个问题。首先,您的 lapply 不会返回数据框列表,而是返回列表列表(只是每个子列表都有一个数据框作为其唯一内容)。

其次,当您将数据框绑定在一起时,它们需要具有相同的列名。在您的情况下,团队名称充当列名称。如果只是覆盖这个,你将不知道一行引用的是哪个团队的数据,所以你需要添加一个“团队”列并将其他列的名称标准化:

library(dplyr)
library(rvest)

team_id <- c(721, 5, 8)
teams   <- paste('http://cfbstats.com/2019/team/', team_id, '/index.html', sep = "")

df_team_stats <- lapply(teams, function(i){
  webpage   <- read_html(i)
  all_stats <- html_nodes(webpage, xpath = "//table[@class='team-statistics']") %>%
               html_table() %>% `[[`(1)
  all_stats$team <- rep(names(all_stats)[2], nrow(all_stats))
  names(all_stats)[1:2] <- c("stat", "home")
  all_stats[,c(4, 1:3)]
})

as_tibble(bind_rows(df_team_stats))
#> # A tibble: 99 x 4
#>    team     stat                                   home          Opponents      
#>    <chr>    <chr>                                  <chr>         <chr>          
#>  1 Air For~ Scoring:  Points/Game                  34.1          19.8           
#>  2 Air For~ Scoring:  Games - Points               13 - 443      13 - 258       
#>  3 Air For~ First Downs:  Total                    286           216            
#>  4 Air For~ First Downs:  Rushing - Passing - By ~ 227 - 52 - 7  77 - 131 - 8   
#>  5 Air For~ Rushing:  Yards / Attempt              5.14          3.49           
#>  6 Air For~ Rushing:  Attempts - Yards - TD        755 - 3881 -~ 375 - 1307 - 11
#>  7 Air For~ Passing:  Rating                       187.92        141.25         
#>  8 Air For~ Passing:  Yards                        1602          2848           
#>  9 Air For~ Passing:  Attempts - Completions - In~ 126 - 68 - 6~ 377 - 238 - 7 ~
#> 10 Air For~ Total Offense:  Yards / Play           6.22          5.53           
#> # ... with 89 more rows

reprex package (v0.3.0) 于 2020 年 6 月 27 日创建

【讨论】:

  • 很棒的东西@AllanCameron 这完美无瑕!如果不是太麻烦,您介意解释以下代码行的工作原理: html_table() %>% [[(1) ------ 特别是 '[['(1) 片段
  • 当然@DonnyDolio。这只是一种使用双括号运算符而不破坏管道和需要临时变量的方法。所以不用B &lt;- func(A); B[[1]]; 你可以做A %&gt;% func() %&gt;% '[['(1)
  • 非常酷@AllanCameron。感谢您的解释和您的所有帮助!了解前进会很有帮助!
【解决方案2】:

也许来自 dplyr 的 bind_cols() 在这里可能有用。所以像:

library(tidyverse)
library(janitor)
library(rvest)

team_id <- c(721, 5, 8)
teams <- paste('http://cfbstats.com/2019/team/', team_id, '/index.html', sep = "")


df_team_stats <- lapply(teams, function(i){
  webpage <- read_html(i)
  team_table <- html_nodes(webpage, 'table.team-statistics')
  overall_stats <- html_table(team_table)[[1]] %>% 
    clean_names() %>% 
    column_to_rownames(var = "x")
})

finaldf <- df_team_stats %>% 
  bind_cols() %>% 
  rownames_to_column(var = "Statistics")

【讨论】:

  • 谢谢@eugene100hickey。这是一个很酷的问题解决方案。感谢您的帮助和知识!
猜你喜欢
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
  • 2018-02-15
相关资源
最近更新 更多