rvest 网络抓取 javascript
rvest web scraping with javascript
我正在尝试使用 rvest
从 FiveThirtyEight 抓取每日预报,但我感兴趣的对象似乎是一个 javascript 对象,我什至很难找到它的位置以及寻找什么。 (我不太精通 CSS 或 Javascript,虽然我在过去几天尝试自学。)
通过检查网页元素和 CSS 选择器,我发现了以下内容:
要看的位置是<div id="polling-avg-chart">
,所以我试了
library(rvest)
url <-
"https://projects.fivethirtyeight.com/election-2016/national-primary-polls/democratic/"
url %>%
read_html() %>%
html_nodes("#polling-avg-chart")
没有太大的成功。输出只是
{xml_nodeset (1)}
[1] <\div id="polling-avg-chart"></div>\n
以圆点表示的个人投票结果在 <g style="clip-path: url("#line-clippoll_avg");"> ... </g>
中,您可以在其中看到 502 个位置的数字。我猜我必须将每个节点的 cx
和 cy
转换为适当的百分比,这是由 <g class="flag-box" transform="translate(30, 161.44093322753096)">...</g>
等完成的。
但是我没有看到预测线的基础数据,不是点。
- 当我将光标悬停在图表上时,我看到
<line class="hover-date-line hide-line">
等内容发生变化,<path class="link" d="M 0 171.40106812500002 C 15 171.40106812500002 15 170.94093803735575 30 170.94093803735575"></path>
等值发生变化,我猜测这些值正在创建每日预测线。
- 但是这些值存储在哪里,以及如何将其转换回诸如“49.1% 克林顿对 26.6% 桑德斯”之类的东西对我来说仍然是个谜。
我确实阅读了其他一些 SO 帖子,例如 this,但其中 none 似乎适用于这个特定问题。在整洁的数据框中获得预测百分比的最佳方法是什么?
那里的图表几乎可以肯定是用 d3.js 或上面的包装器构建的。 d3 非常适合构建基于 svg 的数据可视化,因为它可以帮助您构建比例以将值(例如 40%)映射到屏幕上的位置(例如您看到的内容,例如 cx=100
)。问题是您需要知道这些比例是什么才能取回基础数据,并且比例可能是动态的并且会根据屏幕尺寸等发生变化。
相反,由于数据在下面的 table 中,您可以轻松地抓取它。 table 位于 ID 为 latest-polls
的 div
元素内,并且具有 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))
我正在尝试使用 rvest
从 FiveThirtyEight 抓取每日预报,但我感兴趣的对象似乎是一个 javascript 对象,我什至很难找到它的位置以及寻找什么。 (我不太精通 CSS 或 Javascript,虽然我在过去几天尝试自学。)
通过检查网页元素和 CSS 选择器,我发现了以下内容:
要看的位置是
<div id="polling-avg-chart">
,所以我试了library(rvest) url <- "https://projects.fivethirtyeight.com/election-2016/national-primary-polls/democratic/" url %>% read_html() %>% html_nodes("#polling-avg-chart")
没有太大的成功。输出只是
{xml_nodeset (1)}
[1] <\div id="polling-avg-chart"></div>\n
以圆点表示的个人投票结果在
<g style="clip-path: url("#line-clippoll_avg");"> ... </g>
中,您可以在其中看到 502 个位置的数字。我猜我必须将每个节点的cx
和cy
转换为适当的百分比,这是由<g class="flag-box" transform="translate(30, 161.44093322753096)">...</g>
等完成的。但是我没有看到预测线的基础数据,不是点。
- 当我将光标悬停在图表上时,我看到
<line class="hover-date-line hide-line">
等内容发生变化,<path class="link" d="M 0 171.40106812500002 C 15 171.40106812500002 15 170.94093803735575 30 170.94093803735575"></path>
等值发生变化,我猜测这些值正在创建每日预测线。 - 但是这些值存储在哪里,以及如何将其转换回诸如“49.1% 克林顿对 26.6% 桑德斯”之类的东西对我来说仍然是个谜。
我确实阅读了其他一些 SO 帖子,例如 this,但其中 none 似乎适用于这个特定问题。在整洁的数据框中获得预测百分比的最佳方法是什么?
那里的图表几乎可以肯定是用 d3.js 或上面的包装器构建的。 d3 非常适合构建基于 svg 的数据可视化,因为它可以帮助您构建比例以将值(例如 40%)映射到屏幕上的位置(例如您看到的内容,例如 cx=100
)。问题是您需要知道这些比例是什么才能取回基础数据,并且比例可能是动态的并且会根据屏幕尺寸等发生变化。
相反,由于数据在下面的 table 中,您可以轻松地抓取它。 table 位于 ID 为 latest-polls
的 div
元素内,并且具有 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))