Создайте объект, полученный из очищенных данных, с несколькими уровнями вложенности

Это дополнительный вопрос к тому, который я задавал ранее (Как создать фрейм данных из очищенного веб-сайта, сохраняя вложенную структуру данных ) и ответ от @stefan. Этот ответ идеально подходит для этого вопроса.

Но что, если есть дополнительные уровни вложенности?

library(rvest)
library(dplyr, warn = FALSE)
books <- minimal_html('
  <div class = "entry">
        <div class = "collection">Collection 1</div>
        <div class = "book">
          <div class = "booktitle">Book 1</div>
          <div class = "year">1999</div>
          <div class = "author">
            <div class = "name">Author 1</div>
            <div class = "city">Austin</div>
          </div>  
          <div class = "author">
            <div class = "name">Author 2</div>
            <div class = "city">Dallas</div> 
          </div>  
          <div class = "author">
            <div class = "name">Author 3</div>
            <div class = "city">Memphis</div>  
          </div>  
        </div>
        <div class = "book">
          <div class = "booktitle">Book 2</div>
          <div class = "year">2022</div>
          <div class = "author">
            <div class = "name">Author 4</div>
            <div class = "city">Houston</div>
          </div>  
        </div>
  </div>
  <div class = "entry">  
        <div class = "collection">Collection 2</div>
        <div class = "book">
          <div class = "booktitle">Book 3</div>
          <div class = "year">1845</div>
          <div class = "author">
            <div class = "name">Author 5</div> 
          </div>  
          <div class = "author">
            <div class = "name">Author 6</div>
            <div class = "city">Dayton</div>
          </div>  
          <div class = "author">
            <div class = "name">Author 7</div>
            <div class = "city">Philadelphia</div>  
          </div>  
        </div>
  </div>')

Как и раньше, я бы хотел, чтобы все было на уровне автора, но имя и город автора должны быть в одной строке. Также есть дополнительный внешний слой collection. Все авторы книг в entry должны иметь номер коллекции entry. Таким образом, строк должно быть семь, а Author 7 должен иметь следующие значения: Collection 2, Book 3, 1845, Author 7 и Philadelphia.

Также обратите внимание, что для каждого автора будет только одно (или ни одного) имя и один (или ни одного — у автора 5 нет города) город. А у entry всегда будет ровно один collection.

Как я могу расширить этот код из предыдущего ответа, чтобы получить желаемое решение?

data0 <- books %>%
    html_elements(".book") |>
    lapply(\(x) {
        tibble(
            title = x |> html_element(".booktitle") |> html_text2(),
            year = x |> html_element(".year") |> html_text2(),
            authors = x |> html_elements(".author") |> html_text2(),
        )
    }) |>
    bind_rows()
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
58
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Поскольку между автором и городом есть новый символ строки, вы можете попробовать отдельную функцию из пакета tidyr.

library(tidyr)
tidyr::separate(data0, authors, into=c("authors", "city"), sep = "\\n")

Для более сложных случаев, скорее всего, понадобится пара циклов или рекурсия.

Спасибо за это предложение!

bill999 08.08.2024 16:47

Если есть вложенные слои HTML, вы можете сначала перейти на верхний уровень, используя html_nodes, а затем перейти на нижние уровни.

Глядя на предоставленный HTML-код, можно увидеть слой высшего класса под названием .entry. Сначала мы используем это в html_nodes:

books %>% 
  html_nodes(".entry") %>% 
  lapply(\(x) {
    tibble(
      collection = x |> html_element(".collection") |> html_text2(),
      title = x |> html_element(".booktitle") |> html_text2(),
      year = x |> html_element(".year") |> html_text2(),
      author = x |> html_elements(".author") |> html_text2(),
      city = x |> html_elements(".city") |> html_text2()
    )
  })

Приведенный выше код будет искать классы .author и .city и извлекать соответствующие тексты, но поскольку в одной из строк отсутствует город, функция tibble выдаст сообщение:

Error in `tibble()`:
! Tibble columns must have compatible sizes.
• Size 3: Existing data.
• Size 2: Column `city`.
ℹ Only values of size one are recycled.

Затем мы активизируемся (так же, как в вашем коде):

books %>% 
  html_nodes(".entry") %>% 
  lapply(\(x) {
    tibble(
      collection = x |> html_element(".collection") |> html_text2(),
      title = x |> html_element(".booktitle") |> html_text2(),
      year = x |> html_element(".year") |> html_text2(),
      author = x |> html_elements(".author") |> html_text2()
    ) 
  })

В результате автор и город будут объединены, и мы сможем разделить их:

books %>% 
  html_nodes(".entry") %>% 
  lapply(\(x) {
    tibble(
      collection = x |> html_element(".collection") |> html_text2(),
      title = x |> html_element(".booktitle") |> html_text2(),
      year = x |> html_element(".year") |> html_text2(),
      author = x |> html_elements(".author") |> html_text2()
    ) %>% 
      separate(
        col = author
        ,into = c("authors", "city")
        ,sep = "\n"
        ,fill = "right"
      )
  }) %>% 
  bind_rows()

Спасибо за ответ!

bill999 08.08.2024 16:47
Ответ принят как подходящий

Чтобы опираться на связанный ответ, вы можете добавить несколько xpath для доступа к одноуровневым книгам, здесь это будет элемент коллекции одного и того же родителя.

separate() действительно хороший ярлык в этом случае. В качестве альтернативы обработки сведений об авторе вы можете добавить еще один итератор либо в текущий lapply(), либо переместить его на следующий шаг обработки. Лично я предпочитаю последнее: здесь я сначала добавляю списки узлов авторов в тиббл при переборе всех книг, затем преобразовываю их в подтиблы в mutate() и, наконец, удаляю вложение.

library(rvest)
library(dplyr, warn = FALSE)

books |> 
  html_elements(".book") |>
  lapply(\(x) {
    tibble(
      collection = x |> html_element(xpath = "../div[@class='collection']") |> html_text2(),
      title = x |> html_element(".booktitle") |> html_text2(),
      year = x |> html_element(".year") |> html_text2(),
      authors = x |> html_elements(".author") |> list()
    )
  }) |>
  bind_rows() |> 
  mutate(authors = lapply(authors, \(x) tibble(name = html_element(x, ".name") |> html_text2(),
                                               city = html_element(x, ".city") |> html_text2())))  |> 
  tidyr::unnest(authors, names_sep = ".")
#> # A tibble: 7 × 5
#>   collection   title  year  authors.name authors.city
#>   <chr>        <chr>  <chr> <chr>        <chr>       
#> 1 Collection 1 Book 1 1999  Author 1     Austin      
#> 2 Collection 1 Book 1 1999  Author 2     Dallas      
#> 3 Collection 1 Book 1 1999  Author 3     Memphis     
#> 4 Collection 1 Book 2 2022  Author 4     Houston     
#> 5 Collection 2 Book 3 1845  Author 5     <NA>        
#> 6 Collection 2 Book 3 1845  Author 6     Dayton      
#> 7 Collection 2 Book 3 1845  Author 7     Philadelphia

Пример данных:

books <- minimal_html('
  <div class = "entry">
        <div class = "collection">Collection 1</div>
        <div class = "book">
          <div class = "booktitle">Book 1</div>
          <div class = "year">1999</div>
          <div class = "author">
            <div class = "name">Author 1</div>
            <div class = "city">Austin</div>
          </div>  
          <div class = "author">
            <div class = "name">Author 2</div>
            <div class = "city">Dallas</div> 
          </div>  
          <div class = "author">
            <div class = "name">Author 3</div>
            <div class = "city">Memphis</div>  
          </div>  
        </div>
        <div class = "book">
          <div class = "booktitle">Book 2</div>
          <div class = "year">2022</div>
          <div class = "author">
            <div class = "name">Author 4</div>
            <div class = "city">Houston</div>
          </div>  
        </div>
  </div>
  <div class = "entry">  
        <div class = "collection">Collection 2</div>
        <div class = "book">
          <div class = "booktitle">Book 3</div>
          <div class = "year">1845</div>
          <div class = "author">
            <div class = "name">Author 5</div> 
          </div>  
          <div class = "author">
            <div class = "name">Author 6</div>
            <div class = "city">Dayton</div>
          </div>  
          <div class = "author">
            <div class = "name">Author 7</div>
            <div class = "city">Philadelphia</div>  
          </div>  
        </div>
  </div>')

Created on 2024-08-08 with reprex v2.1.1

Очень мило спасибо!

bill999 08.08.2024 16:46

Другие вопросы по теме