rvest 网络抓取 javascript

rvest web scraping with javascript

我正在尝试使用 rvestFiveThirtyEight 抓取每日预报,但我感兴趣的对象似乎是一个 javascript 对象,我什至很难找到它的位置以及寻找什么。 (我不太精通 CSS 或 Javascript,虽然我在过去几天尝试自学。)

通过检查网页元素和 CSS 选择器,我发现了以下内容:

我确实阅读了其他一些 SO 帖子,例如 this,但其中 none 似乎适用于这个特定问题。在整洁的数据框中获得预测百分比的最佳方法是什么?

那里的图表几乎可以肯定是用 d3.js 或上面的包装器构建的。 d3 非常适合构建基于 svg 的数据可视化,因为它可以帮助您构建比例以将值(例如 40%)映射到屏幕上的位置(例如您看到的内容,例如 cx=100)。问题是您需要知道这些比例是什么才能取回基础数据,并且比例可能是动态的并且会根据屏幕尺寸等发生变化。

相反,由于数据在下面的 table 中,您可以轻松地抓取它。 table 位于 ID 为 latest-pollsdiv 元素内,并且具有 class t-polls.

我将 html_node 与 CSS 选择器一起使用,html_table 将 table 转换为数据框,清理名称并转换数字列到实际的数字列。接下来您还可以做更多事情,例如设置日期格式,但希望这能让您入门。

library(tidyverse)
library(rvest)

url <- "https://projects.fivethirtyeight.com/election-2016/national-primary-polls/democratic/"

polls_df <- url %>% 
  read_html() %>%
  html_node("#latest-polls table.t-polls") %>%
  html_table() %>%
  setNames(c("new", "date", "pollster", "sample_n", "sample_type", names(.)[6:10]) %>% str_remove_all("\W")) %>%
  mutate_at(vars(sample_n, Clinton, Sanders, OMalley), 
      function(x) str_remove_all(x, "\D") %>% as.numeric())

head(polls_df)
#>   new           date                     pollster sample_n sample_type
#> 1   •     Jun. 10-13                 Selzer & Co.      486          LV
#> 2   •     Jun. 26-28                     Fox News      432          RV
#> 3   •     Jun. 18-20                       YouGov      390          LV
#> 4   •     Jun. 15-20              Morning Consult     1733          RV
#> 5   • Jun. 27-Jul. 1                Ipsos, online      142          LV
#> 6   •     Jun. 16-19 Opinion Research Corporation      435          RV
#>   weight      leader Clinton Sanders OMalley
#> 1   1.05  Clinton +2      45      43      NA
#> 2   0.91 Clinton +21      58      37      NA
#> 3   0.79 Clinton +13      55      42      NA
#> 4   0.79 Clinton +18      53      35      NA
#> 5   0.67 Clinton +41      70      29      NA
#> 6   0.66 Clinton +12      55      43      NA

另一种方式是直接抓取资源

在您的浏览器中,打开开发人员工具(F12 in Chrome/Chromium),前往 "Network",刷新(F5),然后寻找格式良好的 JSON。找到它后,我们复制 link 地址(右键单击资源 > 复制 link 地址)。

library(httr)
library(tidyr)
library(purrr)
library(dplyr)
library(ggplot2)

url <- "https://projects.fivethirtyeight.com/election-2016/national-primary-polls/USA.json"

r <- GET(url)

所有数据都在那里。权重也是如此,因此您可能可以重新计算这些平均值。绘制的数据位于 "model":

dat <- 
  jsonlite::fromJSON(content(r, as = "text")) %>% 
  map(purrr::pluck, "model") %>% 
  bind_rows(.id = "party") %>% 
  mutate_all(readr::parse_guess)

# # A tibble: 5,288 x 5
#    party candidate_name state forecastdate poll_avg
#    <chr> <chr>          <chr> <date>          <dbl>
#  1 D     Sanders        USA   2016-07-01       36.5
#  2 D     Clinton        USA   2016-07-01       55.4
#  3 D     Sanders        USA   2016-06-30       37.0
#  4 D     Clinton        USA   2016-06-30       54.6
#  5 D     Sanders        USA   2016-06-29       37.0
#  6 D     Clinton        USA   2016-06-29       54.9
#  7 D     Sanders        USA   2016-06-28       37.2
#  8 D     Clinton        USA   2016-06-28       54.4
#  9 D     Sanders        USA   2016-06-27       37.4
# 10 D     Clinton        USA   2016-06-27       53.9
# # ... with 5,278 more rows

重现图表:

dat %>% 
  filter(candidate_name %in% c("Clinton", "Kasich", "Sanders", "Trump")) %>% 
  ggplot(aes(forecastdate, poll_avg)) +
  geom_line(aes(col = candidate_name)) +
  facet_wrap(~party)

如果您想要互动:

library(dygraphs)
library(htmltools)

foo <- dat %>% 
  filter(candidate_name %in% c("Clinton", "Kasich", "Sanders", "Trump")) %>% 
  split(.$party) %>% 
  map(~ {
    select(.x, forecastdate, candidate_name, poll_avg) %>% 
      spread(candidate_name, poll_avg) %>% 
      {xts(.[-1], .[[1]])} %>%
      dygraph(group = "poll-model") %>% 
      dyRangeSelector()
  })

browsable(tagList(foo))