首页 > 解决方案 > 抓取不同网站中元素数量变化的自由文本

问题描述

我正在抓取以这种格式提供的公司数据库,其中每家公司都位于不同的网站下,由 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_code并且variable_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 选择器获得,正如许多地方所解释的那样(例如hereherehere)。显然,我无法更改 html 代码。

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

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

标签: rweb-scrapingrvest

解决方案


我会做这样的事情:

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)

推荐阅读