如何使用Rvest抓取包含嵌套列的HTML表格?

3

我在抓取带有嵌套列的HTML表格方面遇到了一个大问题。

这个表格来自香港入境事务处

截图如下:

enter image description here

我尝试使用rvest来完成,但结果很混乱。

library(rvest)
library(tidyverse)
library(stringr)
library(dplyr)

url_data <- "https://www.immd.gov.hk/eng/stat_20220901.html"

url_data %>%
read_html()
css_selector <- "body > section:nth-child(7) > div > div > div > div > table"
immiTable <- url_data %>% 
read_html() %>% html_element(css = css_selector) %>% html_table()
immiTable

enter image description here

我的目标是提取第一行(即机场)并将其绘制成饼图,并生成整个表的数据框并保存到Excel中。
我意识到关于展开表格和抓取嵌套表格的教材相当缺乏。因此,我需要您的指导。非常感谢您的帮助。
2个回答

3
这里有一种方法。头部格式使事情变得复杂,但下面的代码可以工作。它可以提取整个表格,而不仅仅是第一行。
suppressPackageStartupMessages({
  library(rvest)
  library(dplyr)
  library(ggplot2)
})

url_data <- "https://www.immd.gov.hk/eng/stat_20220901.html"

page <- url_data %>% read_html()

page %>%
  html_elements("[headers='Arrival']") %>%
  html_text() %>%
  paste("Arrival", .) -> col_names
page %>%
  html_elements("[headers='Departure']") %>%
  html_text() %>%
  paste("Departure", .) %>%
  c(col_names, .) -> col_names
page %>%
  html_elements("[headers='Control_Point']") %>%
  html_text() -> row_names
page %>%
  html_elements("[class='hRight']") %>%
  html_text() %>%
  sub(",", "", .) %>%
  as.numeric() %>%
  matrix(nrow = length(row_names), byrow = TRUE) %>%
  as.data.frame() %>%
  setNames(col_names) %>%
  `row.names<-`(row_names) -> final

final
#>                                Arrival Hong Kong Residents
#> Airport                                               4258
#> Express Rail Link West Kowloon                           0
#> Hung Hom                                                 0
#> Lo Wu                                                    0
#> Lok Ma Chau Spur Line                                    0
#> Heung Yuen Wai                                           0
#> Hong Kong-Zhuhai-Macao Bridge                          333
#> Lok Ma Chau                                              0
#> Man Kam To                                               0
#> Sha Tau Kok                                              0
#> Shenzhen Bay                                          3404
#> China Ferry Terminal                                     0
#> Harbour Control                                          0
#> Kai Tak Cruise Terminal                                  0
#> Macau Ferry Terminal                                     0
#> Total                                                 7995
#>                                Arrival Mainland Visitors Arrival Other Visitors
#> Airport                                             1488                    422
#> Express Rail Link West Kowloon                         0                      0
#> Hung Hom                                               0                      0
#> Lo Wu                                                  0                      0
#> Lok Ma Chau Spur Line                                  0                      0
#> Heung Yuen Wai                                         0                      0
#> Hong Kong-Zhuhai-Macao Bridge                         28                     39
#> Lok Ma Chau                                            0                      0
#> Man Kam To                                             0                      0
#> Sha Tau Kok                                            0                      0
#> Shenzhen Bay                                         348                     37
#> China Ferry Terminal                                   0                      0
#> Harbour Control                                        0                      0
#> Kai Tak Cruise Terminal                                0                      0
#> Macau Ferry Terminal                                   0                      0
#> Total                                               1864                    498
#>                                Arrival Total Departure Hong Kong Residents
#> Airport                                 6168                          3775
#> Express Rail Link West Kowloon             0                             0
#> Hung Hom                                   0                             0
#> Lo Wu                                      0                             0
#> Lok Ma Chau Spur Line                      0                             0
#> Heung Yuen Wai                             0                             0
#> Hong Kong-Zhuhai-Macao Bridge            400                           243
#> Lok Ma Chau                                0                             0
#> Man Kam To                                 0                             0
#> Sha Tau Kok                                0                             0
#> Shenzhen Bay                            3789                          1301
#> China Ferry Terminal                       0                             0
#> Harbour Control                            0                             0
#> Kai Tak Cruise Terminal                    0                             0
#> Macau Ferry Terminal                       0                             0
#> Total                                  10357                          5319
#>                                Departure Mainland Visitors
#> Airport                                               1154
#> Express Rail Link West Kowloon                           0
#> Hung Hom                                                 0
#> Lo Wu                                                    0
#> Lok Ma Chau Spur Line                                    0
#> Heung Yuen Wai                                           0
#> Hong Kong-Zhuhai-Macao Bridge                          194
#> Lok Ma Chau                                              0
#> Man Kam To                                               0
#> Sha Tau Kok                                              0
#> Shenzhen Bay                                           524
#> China Ferry Terminal                                     0
#> Harbour Control                                          0
#> Kai Tak Cruise Terminal                                  0
#> Macau Ferry Terminal                                     0
#> Total                                                 1872
#>                                Departure Other Visitors Departure Total
#> Airport                                             315            5244
#> Express Rail Link West Kowloon                        0               0
#> Hung Hom                                              0               0
#> Lo Wu                                                 0               0
#> Lok Ma Chau Spur Line                                 0               0
#> Heung Yuen Wai                                        0               0
#> Hong Kong-Zhuhai-Macao Bridge                        15             452
#> Lok Ma Chau                                           0               0
#> Man Kam To                                            0               0
#> Sha Tau Kok                                           0               0
#> Shenzhen Bay                                         28            1853
#> China Ferry Terminal                                  0               0
#> Harbour Control                                       0               0
#> Kai Tak Cruise Terminal                               0               0
#> Macau Ferry Terminal                                  0               0
#> Total                                               358            7549

该内容于2022-09-18使用reprex v2.0.2创建


要在ggplot中绘制饼图,请先绘制条形图,然后更改为极坐标。

Airport <- final[1,,]
Airport %>%
  t() %>%
  as.data.frame() %>%
  mutate(`Arrival/Departure` = row.names(.)) %>%
  ggplot(aes("", Airport, fill = `Arrival/Departure`)) +
  geom_col(width = 1) +
  scale_fill_manual(values = RColorBrewer::brewer.pal(n = 8, name = "Spectral")) +
  coord_polar(theta = "y", start = 0) +
  theme_void()

使用 reprex v2.0.2 工具于2022-09-18创建


你救了我的命 @Rui Barradas!我想知道诀窍,也就是说,你的语法如何去掉前四行标题?看起来你所做的是,首先抓取数字,其次添加行和列名称。我理解得对吗? - ronzenith
也许我的问题应该这样表达:如何在不提取列名的情况下抓取表格?@ruibarradas - ronzenith
@ronzenith,区别在于要提取的元素不同,对于数字,它是class='hRight',而对于标题,则是headers='Arrival'Departure',然后是headers='Control_Point'(行名称)。 - Rui Barradas

2
另一种方法是选择tbody行,通过属性过滤掉隐藏的项目,然后稍后添加标题。
library(rvest)
library(tidyverse)

rows <- read_html("https://www.immd.gov.hk/eng/stat_20220901.html") %>% html_elements(".table-passengerTrafficStat tbody tr")
prefixes <- c("arr", "dep")
cols <- c("Hong Kong Residents", "Mainland Visitors", "Other Visitors", "Total")
headers <- c("Control_Point", crossing(prefixes, cols) %>% unite("headers", 1:2, remove = T) %>% unlist() %>% unname())

df <- map_dfr(
  rows,
  function(x) {
    x %>%
      html_elements("td[headers]") %>%
      set_names(headers) %>%
      html_text()
  }
) %>%
  mutate(across(c(-1), ~ str_replace(.x, ",", "") %>% as.integer()))

或者略微简化,
library(rvest)
library(tidyverse)

rows <- read_html("https://www.immd.gov.hk/eng/stat_20220901.html") %>% html_elements(".table-passengerTrafficStat tbody tr")
prefixes <- c("arr", "dep")
cols <- c("Hong Kong Residents", "Mainland Visitors", "Other Visitors", "Total")
headers <- c("Control_Point", crossing(prefixes, cols) %>% unite("headers", 1:2, remove = T) %>% unlist() %>% unname())

df <- map_dfr(rows, ~ set_names(.x %>% html_elements("td[headers]") %>% html_text(), headers)) %>%
  mutate(across(c(-1), ~ str_replace(.x, ",", "") %>% as.integer()))

非常感谢您的帮助。当我仔细阅读您的代码时,我不太理解编写额外函数 function(x) { x %>% html_elements("td[headers]") %>% set_names(headers) %>% html_text() } 的用途。(1) 这里的x是什么意思?(2) 为什么该函数可以自运行而无需调用函数并告诉R x是什么?@qharr - ronzenith
我想逐行应用,因此需要逐行调用函数,在这种情况下使用 map_dfr。x 是从左侧传递的内容(行列表),传递到右侧(应用于每一行的函数)。因此,x 是单个行。函数由 map_dfr 应用。 - QHarr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接