用 rvest 从 html 中抓取对象

Scrape object from html with rvest

我是使用 r 进行网络抓取的新手,我正在尝试获取可能不是文本的每日更新对象。 url 是 here 并且我想在页面末尾提取日常情况 table 。这个对象的class是

class="aem-GridColumn aem-GridColumn--default--12 aem-GridColumn--offset--default--0"

我对 html 和 css 没有真正的经验,所以如果您对我如何从网页中提取对象有任何有用的资源或建议,我将不胜感激,因为 SelectorGadget 在这种情况下指示“未找到有效路径。”

如果不从事编写网络爬虫的工作,我认为这应该对您有所帮助:

library(rvest)
url = 'https://covid19.public.lu/en.html'
source = read_html(url)
selection = html_nodes( source , '.cmp-gridStat__item-container' ) %>% html_node( '.number' ) %>% html_text() %>% toString()

可能有更优雅的方法可以有效地做到这一点,但当我需要像这样的蛮力时,我会尝试将其分解成小部分。

  1. 使用 httr 库获取原始 html。
  2. 使用str_extract from the stringr库从html中提取特定的数据。
  3. 我同时使用 positive lookbehind and lookahead 正则表达式来获取我需要的确切数据。它基本上采用 "?<=text_right_before).+?(?=text_right_after)
  4. 的形式
library(httr)
library(stringr)

r <- GET("https://covid19.public.lu/en.html")
html<-content(r, "text")

normal_care=str_extract(html, regex("(?<=Normal care: ).+?(?=<br>)"))
intensive_care=str_extract(html, regex("(?<=Intensive care: ).+?(?=</p>)"))

我们可以使用vroom

转换从Daily situation update获得的文本
library(rvest)
library(vroom)

url = 'https://covid19.public.lu/en.html'
df = url %>%
  read_html() %>% 
  html_nodes('.cmp-gridStat__item-container') %>% 
  html_text2()

vroom(df, delim = '\n', col_names = F)

# A tibble: 22 x 1
   X1                                     
   <chr>                                  
 1 369 People tested positive for COVID-19
 2 Per 100.000 inhabitants: 58,13         
 3 Unvaccinated: 91,20  

编辑:

html_element 对比 html_elemnts

html_elemnts (html_nodes) 的噘嘴是,

[1] "369 People tested positive for COVID-19\n\nPer 100.000 inhabitants: 58,13\n\nUnvaccinated: 91,20\n\nVaccinated: 41,72\n\nRatio Unvaccinated / Vaccinated: 2,19\n\n "
[2] "4 625 Number of PCR tests performed\n\nPer 100.000 inhabitants: 729\n\nPositivity rate in %: 7,98\n\nReproduction rate: 0,97"                                       
[3] "80 Hospitalizations\n\nNormal care: 57\nIntensive care: 23\n\nNew deaths: 1\nTotal deaths: 890"                                                                     
[4] "6 520 Vaccinations per day\n\nDose 1: 785\nDose 2: 468\nComplementary dose: 5 267"                                                                                  
[5] "960 315 Total vaccines administered\n\nDose 1: 452 387\nDose 2: 395 044\nComplementary dose: 112 884" 

html_element (html_node)` 的

[1] "369 People tested positive for COVID-19\n\nPer 100.000 inhabitants: 58,13\n\nUnvaccinated: 91,20\n\nVaccinated: 41,72\n\nRatio Unvaccinated / Vaccinated: 2,19\n\n "

如您所见,html_nodes returns 所有与节点关联的值,而 html_node 仅 returns 第一个节点。因此,前者会为您获取真正有用的所有节点。

html_text 对比 html_text2

html_text2保留了字符串中的断点,通常是\n\b。这些在处理字符串时很有用。

更多信息在 rvest 文档中, https://cran.r-project.org/web/packages/rvest/rvest.pdf

我想知道您是否可以从他们的任何 public API 中获得相同的数据。如果您只是想要一个带有 table 的 pdf(加上许多其他 table 有用的信息),您可以使用 API 来提取。

如果你想要一个 DataFrame(类似于网页),你可以编写一个用户定义的函数,在 pdftools 的帮助下,从 pdf 重建 table。需要付出更多努力,但由于您已经有了其他关于使用 rvest 的答案,我想我会看看这个。我查看了 tabularize,但这并不是特别有效。

更有可能的是,您可以将几个 API 数据集放在一起以获得完整内容,而无需解析我使用的 pdf publication,例如有一个 Excel 电子表格给出了案例编号。

N.B。网页中有一些底部计算未包括在下面。我只处理了 pdf 中的测试信息 table。


Rapports 记者:

https://data.public.lu/en/datasets/covid-19-rapports-journaliers/#_ https://download.data.public.lu/resources/covid-19-rapports-journaliers/20211210-165252/coronavirus-rapport-journalier-10122021.pdf

API 数据集:

https://data.public.lu/api/1/datasets/#


library(tidyverse)
library(jsonlite)
## https://data.library.virginia.edu/reading-pdf-files-into-r-for-text-mining/
# install.packages("pdftools")
library(pdftools)

r <- jsonlite::read_json("https://data.public.lu/api/1/datasets/#")
report_index <- match(TRUE, map(r$data, function(x) x$slug == "covid-19-rapports-journaliers"))
latest_daily_covid_pdf <- r$data[[report_index]]$resources[[1]]$latest # coronavirus-rapport-journalier

filename <- "covd_daily.pdf"

download.file(latest_daily_covid_pdf, filename, mode = "wb")

get_latest_daily_df <- function(filename) {
  
  data <- pdf_text(filename)

  text <- data[[1]] %>% strsplit(split = "\n{2,}")

  web_data <- text[[1]][3:12]

  df <- map(web_data, function(x) strsplit(x, split = "\s{2,}")) %>%
    unlist() %>%
    matrix(nrow = 10, ncol = 5, byrow = T) %>%
    as_tibble()

  colnames(df) <- text[[1]][2] %>%
    strsplit(split = "\s{2,}") %>%
    map(function(x) gsub("(.*[a-z])\d+", "\1", x)) %>%
    unlist()

  title <- text[[1]][1] %>%
    strsplit(split = "\n") %>%
    unlist() %>%
    tail(1) %>%
    gsub("\s+", " ", .) %>%
    gsub(" TOTAL", "", .)

  colnames(df)[2:3] <- colnames(df)[2:3] %>% paste(title, ., sep = " ")
  colnames(df)[4:5] <- colnames(df)[4:5] %>% paste("TOTAL", ., sep = " ")
  colnames(df)[1] <- "Metric"

  clean_col <- function(x) {
    gsub("\s+|,", "", x) %>% as.numeric()
  }

  clean_col2 <- function(x) {
    gsub("\n", " ", gsub("([a-z])(\d+)", "\1", x))
  }

  df <- df %>% mutate(across(.cols = -c(colnames(df)[1]), clean_col),
    Metric = clean_col2(Metric)
  )

  return(df)
}


View(get_latest_daily_df(filename))

输出:


备用:

如果您只想提取项目然后处理,您可以将每一列提取为列表中的项目。替换 br 元素,使这些元素中的内容以逗号分隔的列表结尾:

library(rvest)
library(magrittr)
library(stringi)
library(xml2)

page <- read_html("https://covid19.public.lu/en.html")
xml_find_all(page, ".//br") %>% xml_add_sibling("span", ",") #This method from  @hrbrmstr
xml_find_all(page, ".//br") %>% xml_remove()

columns <- page %>% html_elements(".cmp-gridStat__item")

map(columns, ~ .x %>%
  html_elements("p") %>%
  html_text(trim = T) %>%
  gsub("\n\s{2,}", " ", .)
  %>%
  stri_remove_empty())