抓取不同网站中元素数量不断变化的自由文本

Scrape free text that has changing number of elements in different websites

我正在抓取 this format 中可用的公司数据库,其中每个公司都在不同的网站下,由 url 末尾的数字定义(上面的示例是 15310;请参阅url)。我正在使用 rvest.

我想提取“Organización”下显示的所有条目。每个变量名都以粗体显示,后面跟着普通文本中的值。在上面的示例中,有 16 个变量要提取。

这些网站有两个问题:

  1. 普通文本不在元素内(而变量名称在)。实际上,代码是这样的(注意“值”在 label 之外):
     <div class="form-group">
         <label for="variable_code">variable_name</label>
         Value
     </div>
  1. 并非所有公司都有相同数量的变量(上例中为 16 个)。有些人拥有更多,有些人拥有更少。尽管如此,variable_codevariable_name 在整个数据库中都是相同的。

我可以想到两种方法来抓取数据。一种是根据固定位置来抓取。为此,我可以使用“nth-child”类型的 CSS 选择器来获取每个变量。但是,由于不同公司的变量数量不同,我需要将变量名称和值都保存为 R 变量。如下代码所示(对于一个网站;对于更多只需要添加循环,此处无关紧要):

library(xml2)
library(rvest)
library(stringr)

url <- "https://tramites.economia.gob.cl/Organizacion/Details/15310"

webpage <- read_html(url) #read webpage

title_html <- html_nodes(webpage, "body > div > div:nth-child(5) > div > div:nth-child(1) > div:nth-child(3)") # this selects by element in division, after which I extract both the variable name and value as elements. Ideally, you want only the value, to allocate to a variable in a dataframe.

title <- html_text(title_html)

variable_name <- trimws(strsplit(title, "\r\n")[[1]][2])
value <- trimws(strsplit(title, "\r\n")[[1]][3])

所以,上面的方法可行,但是很耗时,因为它将变量名保存为变量,之后我需要转换数据。

另一种选择是根据标签抓取。即,搜索代码中的每个变量并获取其值。类似于:

title_html <- html_nodes(webpage, "body > div > div:nth-child(5) > div > div:nth-child(1) label[for=RazonSocial]") 

这种方法的问题是每个变量的值都是自由文本(即在特定元素之外)。因此,它不能通过 CSS 选择器获得,正如许多地方所解释的那样(例如 here, here, or here)。显然,我无法更改 html 代码。

我可以做些什么来改进抓取过程?我是否坚持使用蛮力,第一种方法,将所有内容提取为变量?或者我能以某种方式提高效率吗?

PS:我想到的一种方法是使用第二种方法以某种方式获取找到标签的位置,然后使用第一种方法获取值。但我怀疑 R 是否有此选项(如 excel 中的 addresscell)。

我会这样做:

library(xml2)
library(rvest)
library(tidyverse)

url <- "https://tramites.economia.gob.cl/Organizacion/Details/15310"

webpage <- read_html(url)

# select every 'cell'
form_groups <- webpage %>% 
  html_nodes("div.form-group")

# select cell names (label or strong)
form_groups_labels <- form_groups %>% 
  html_node("label, strong") %>% 
  html_text()

# select cell contents (text following <br>, or inside p)
form_groups_values <- form_groups %>% 
  html_node(xpath = "br/following-sibling::text()|p") %>% 
  html_text(trim = T)

# store it in a table
df <- tibble(
  id = 15310,
  labels = form_groups_labels,
  values = form_groups_values
)

# make it wider if required (after you've gathered all organizations)
df %>% 
  pivot_wider(id_cols = id,
              names_from = labels,
              values_from = values)