Исправьте next_req() для пагинации запроса API с использованием httr2?

Вопрос или проблема

Я хочу итеративно вызывать пагинированный API с помощью функции req_perform_iterative библиотеки httr2, где ответ предоставляет следующий URL для запроса.

Однако мне не удается правильно сформировать аргумент next_req() или использовать помощники по итерации, такие как iterate_with_cursor(), а примеры в документации довольно ограничены. В моем случае мне нужно использовать следующий URL, а не сдвигать номер страницы, так как именно такая система пагинации в API, который я вызываю.

Может кто-то помочь мне правильно сформировать функцию next_req()?

Мы можем использовать API Рика и Морти в качестве примера:

library(httr2)

# запрос одной страницы
req <- request("https://rickandmortyapi.com/api/character?page=1") |>
  req_perform() |>
  resp_body_json()

# вернуть URL для следующей страницы
next_url <- req$info$"next"

Как я могу превратить это в рабочую функцию req_perform_iterative(), которая будет возвращать несколько страниц? Спасибо!

Хотя вы могли бы использовать iterate_with_cursor() здесь, это бы лучше подошло, если бы next в теле ответа был просто номером следующей страницы.

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

library(httr2)

# существующий помощник итерации, следует за URL, найденным в заголовке Link:
iterate_with_link_url()
#> function (resp, req) 
#> {
#>     url <- resp_link_url(resp, rel)
#>     if (!is.null(url)) {
#>         req %>% req_url(url)
#>     }
#> }

# пользовательский помощник, основанный на iterate_with_link_url(),
# следует за следующим URL из тела ответа
iterate_with_body_info_next <- function(resp, req) {
  url <- resp_body_json(resp)$info$`next`
  if (!is.null(url)) {
    req %>% req_url(url)
  }
}

resps <- 
  request("https://rickandmortyapi.com/api/character") |> 
  req_perform_iterative(
    next_req = iterate_with_body_info_next,
    max_reqs = 3
)
resps
#> [[1]]
#> <httr2_response>
#> GET https://rickandmortyapi.com/api/character
#> Status: 200 OK
#> Content-Type: application/json
#> Body: В памяти (19496 байтов)
#> 
#> [[2]]
#> <httr2_response>
#> GET https://rickandmortyapi.com/api/character?page=2
#> Status: 200 OK
#> Content-Type: application/json
#> Body: В памяти (10380 байтов)
#> 
#> [[3]]
#> <httr2_response>
#> GET https://rickandmortyapi.com/api/character?page=3
#> Status: 200 OK
#> Content-Type: application/json
#> Body: В памяти (9723 байтов)

# объект info из тела первого запроса
str(resp_body_json(resps[[1]])$info)
#> Список из 4
#>  $ count: int 826
#>  $ pages: int 42
#>  $ next : chr "https://rickandmortyapi.com/api/character?page=2"
#>  $ prev : NULL

Хотя iterate_with_offset() также будет работать нормально, определите параметр URL, который будет увеличиваться, и функцию, которая извлекает общее количество страниц из первого запроса, и в итоге вы получите что-то вроде этого:

request("https://rickandmortyapi.com/api/character") |> 
  req_perform_iterative(
    next_req = iterate_with_offset(
      param_name = "page",
      resp_pages = \(resp) resp_body_json(resp)$info$pages
    ),
    max_reqs = 3
  )

Создано 18 октября 2024 года с помощью reprex v2.1.1

Ответ или решение

Для выполнения итерированного обращения к API с использованием библиотеки httr2 в R, где отклик API предоставляет URL следующей страницы, необходимо правильно реализовать функцию next_req(). Ниже приведены пошаговые инструкции и пример кода с подробным объяснением.

Шаг 1: Подключение библиотеки

Первое, что необходимо сделать – это загрузить библиотеку httr2. Убедитесь, что библиотека установлена в вашей рабочей среде.

library(httr2)

Шаг 2: Определение функции для итеративного извлечения URL

Вам нужно создать кастомную функцию, которая будет извлекать URL следующей страницы из ответа API. Используя приведенный вами код как основу, мы создадим новую функцию, которая будет обрабатывать JSON отклик и извлекать нужный адрес.

# Кастомная функция для извлечения URL следующей страницы из тела ответа
iterate_with_body_info_next <- function(resp, req) {
  # Извлекаем JSON-ответ
  next_url <- resp_body_json(resp)$info$next
  # Проверяем, не равен ли URL NULL
  if (!is.null(next_url)) {
    # Если URL существует, обновляем запрос
    req %>% req_url(next_url)
  }
}

Шаг 3: Выполнение итеративного запроса

Теперь, когда у вас есть функция для извлечения следующего URL, можно использовать req_perform_iterative() для итеративного выполнения запросов к API. Вот как можно это сделать:

# Выполняем итерированные запросы к API Rick and Morty
resps <- request("https://rickandmortyapi.com/api/character") |>
  req_perform_iterative(
    next_req = iterate_with_body_info_next,
    max_reqs = 3  # Ограничиваем количество запросов
  )

# Проверяем результаты
resps

Пояснение кода

  1. Библиотека: Мы подключаем httr2, что позволяет работать с HTTP запросами в R.
  2. iterate_with_body_info_next: Это функция, которая определяет, как получать следующий URL из ответа API. Она проверяет наличие переданного URL и, если он существует, обновляет текущий запрос.
  3. req_perform_iterative: Эта функция запрашивает данные, используя наш кастомный метод next_req. Мы ограничиваем запросы до 3-х, чтобы избежать слишком большого количества обращений и не захламлять память.

Заключение

Используя этот подход, вы можете эффективно обрабатывать API с постраничной навигацией, основанной на динамически генерируемых URL, как в случае с API Rick and Morty. Вышеуказанный код позволяет легко расширять его в будущем, если API изменится или добавит новые параметры.

Не забудьте протестировать вашу реализацию для проверки корректности работы, особенно на случай изменения формата ответа от API.

Оцените материал
Добавить комментарий

Капча загружается...