R Academy · 강의

페이지 매김과 여러 페이지 처리

페이지가 나뉜 결과를 순회하고 스크랩한 데이터를 하나의 데이터 세트로 결합합니다.

레슨 4/413개 단계

페이지 매김과 여러 페이지 처리은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 R Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

페이지 매김 이해하기

많은 웹사이트는 콘텐츠를 여러 페이지로 나눕니다. URL 기반 페이지 매김은 URL에 페이지 번호를 추가합니다(예: ?page=2 또는 /page/2/). 패턴을 파악하여 프로그래밍 방식으로 URL을 구성하세요.

library(rvest)

# Common URL pagination patterns:
# Pattern 1: query parameter
# https://site.com/products?page=1
# https://site.com/products?page=2

# Pattern 2: path segment
# https://site.com/products/page/1/
# https://site.com/products/page/2/

# Build URLs for pages 1 to 5
base_url <- 'https://books.toscrape.com/catalogue/page-'
page_urls <- paste0(base_url, 1:5, '.html')
page_urls
# [1] 'https://books.toscrape.com/catalogue/page-1.html'
# [2] 'https://books.toscrape.com/catalogue/page-2.html' ...

sprintf로 URL 패턴 만들기

sprintf()와 paste0()은 모두 URL 문자열을 만들지만, sprintf()는 0으로 채우기와 서식 지정에 대해 더 세밀하게 제어할 수 있습니다. 사이트의 URL 구조에 맞는 함수를 사용하세요.

# Various URL building approaches

# paste0 for simple concatenation
pages <- 1:5
urls_v1 <- paste0('https://site.com/list?p=', pages)
urls_v1[1]  # 'https://site.com/list?p=1'

# sprintf for formatted strings
urls_v2 <- sprintf('https://site.com/page/%02d/', pages)
urls_v2[1]  # 'https://site.com/page/01/'

# With multiple parameters
urls_v3 <- sprintf('https://site.com/items?page=%d&size=20', pages)
urls_v3[3]  # 'https://site.com/items?page=3&size=20'

# glue package alternative
# glue('https://site.com/page/{pages}')

map()으로 여러 페이지 스크래핑하기

purrr::map()은 URL 벡터에 걸쳐 스크래핑 함수를 적용하고 결과 목록을 반환합니다. dplyr::bind_rows()와 함께 사용하면 하나의 데이터 프레임으로 병합할 수 있습니다.

library(rvest)
library(purrr)

# Define a function to scrape one page
scrape_page <- function(url) {
  # In real code: page <- read_html(url)
  # Here we simulate with local HTML
  page <- read_html(sprintf(
    '<ul><li class="item">Item %d-A</li><li class="item">Item %d-B</li></ul>',
    url, url
  ))
  data.frame(
    title = html_text2(html_elements(page, '.item')),
    page  = url
  )
}

# Apply over pages 1 and 2 (simulated with numbers)
results <- map(1:2, scrape_page)
do.call(rbind, results)

예의 바른 스크래핑: Sys.sleep()

서버에 빠르게 연속으로 요청을 보내는 것은 예의에 어긋나며 요청 제한이나 IP 차단을 유발할 수 있습니다. 서버를 배려하여 요청 사이에 Sys.sleep()을 추가하세요. 일반적으로 1~2초의 지연이면 충분합니다.

library(rvest)

# Polite scraping with delays between requests
scrape_with_delay <- function(urls, delay = 1.5) {
  results <- vector('list', length(urls))
  for (i in seq_along(urls)) {
    cat(sprintf('Scraping page %d of %d\n', i, length(urls)))
    # results[[i]] <- read_html(urls[[i]])
    Sys.sleep(delay)  # Wait before next request
  }
  results
}

# Also: randomize delay to appear more human-like
random_delay <- function(min = 1, max = 3) {
  Sys.sleep(runif(1, min = min, max = max))
}
random_delay()
cat('Always be polite to servers you scrape')

사용자 에이전트 설정하기

설명적인 User-Agent 헤더를 설정하여 스크래퍼의 신원을 예의 바르게 밝히세요. 서버는 이를 사용하여 봇을 식별합니다. 좋은 User-Agent에는 사이트 운영자가 연락할 수 있도록 이름과 연락처 정보가 포함됩니다.

library(rvest)

# Default user agent reveals R/rvest
# Better: set a descriptive, honest UA
ua <- paste0(
  'MyResearchBot/1.0 ',
  '(Academic research; ',
  'contact: researcher@university.edu)'
)

# With httr/httr2 for UA control:
# library(httr2)
# response <- request(url) |>
#   req_headers('User-Agent' = ua) |>
#   req_perform()
# html <- read_html(resp_body_string(response))

# Or use polite package for full compliance:
# library(polite)
# session <- bow('https://example.com', user_agent = ua)
cat('Honest UA: better for everyone')

robots.txt 준수하기

robots.txt는 크롤러가 접근할 수 없는 경로를 알려 줍니다. robotstxt 패키지를 사용하면 스크래핑하기 전에 URL에 대한 접근이 허용되는지 프로그래밍 방식으로 확인할 수 있습니다.

library(rvest)
# library(robotstxt)  # uncomment to use

# Check if scraping is allowed:
# rtxt <- robotstxt(domain = 'books.toscrape.com')
# rtxt$check(paths = '/catalogue/', bot = '*')
# TRUE means allowed

# Robots.txt typically looks like:
# User-agent: *
# Disallow: /admin/
# Disallow: /private/
# Allow: /catalogue/
# Crawl-delay: 2

# The polite package combines UA + robots.txt + delays:
# library(polite)
# session <- bow('https://books.toscrape.com')
# page <- scrape(session)
cat('Always check robots.txt before large-scale scraping')

페이지 매김 링크 따라가기

URL을 추측하는 대신 각 페이지의 실제 '다음' 링크를 따라가세요. html_attr(html_element(page, 'a.next'), 'href')는 다음 페이지 링크를 동적으로 찾아 비순차적인 페이지 매김 패턴도 처리합니다.

library(rvest)

# Pattern: follow 'next' links dynamically
scrape_all_pages <- function(start_url, max_pages = 10) {
  all_data <- list()
  url <- start_url
  i <- 1
  while (!is.null(url) && i <= max_pages) {
    # page <- read_html(url)
    # data <- extract_data(page)
    # all_data[[i]] <- data
    # next_link <- html_attr(html_element(page, 'li.next a'), 'href')
    # url <- if (!is.na(next_link)) xml2::url_absolute(next_link, url)
    #        else NULL
    cat(sprintf('Would scrape page %d: %s\n', i, url))
    url <- NULL  # Stop in this demo
    i <- i + 1
  }
  do.call(rbind, all_data)
}
scrape_all_pages('https://books.toscrape.com')

rvest에서 session_follow_link() 사용하기

rvest의 session()은 쿠키와 기록을 유지하는 상태 저장 탐색 세션을 만듭니다. session_follow_link()는 링크로 이동하며, 로그인이나 페이지 간 상태 유지가 필요한 사이트에 유용합니다.

library(rvest)
# Stateful session with rvest
# s <- session('https://example.com/login')
# s <- session_submit(s,
#   html_form(read_html(s))[[1]],
#   list(username='me', password='pw')
# )
# After login, navigate:
# page2 <- session_follow_link(s, 'Products')
# page3 <- session_back(page2)   # go back
# session_history(page2)         # show history

# For simple cases, follow href directly:
# href <- html_attr(html_element(page, 'a.next'), 'href')
# next_page <- session_jump_to(s, href)
cat('session() maintains cookies across requests')

마지막 페이지 감지하기

중지할 시점을 파악해야 합니다. '다음' 버튼이 있는지, 현재 페이지의 항목 수가 예상보다 적은지, 페이지 번호가 알려진 전체 개수를 초과했는지 확인하세요. 예외적인 경우도 적절하게 처리해야 합니다.

library(rvest)
# Strategy 1: Check if 'Next' link exists
has_next <- function(page) {
  next_link <- html_element(page, 'li.next a')
  !is.na(html_attr(next_link, 'href'))
}

# Strategy 2: Compare item count to expected page size
page_size <- 20
check_done <- function(items_on_page) {
  items_on_page < page_size
}

# Strategy 3: Read total from pagination text
# e.g., 'Page 1 of 50' -> extract 50
extract_total_pages <- function(page) {
  text <- html_text2(html_element(page, '.current'))
  as.integer(gsub('.*of (\\d+).*', '\\1', text))
}
cat('Always plan your stopping condition')

스크래핑 오류 처리하기

네트워크 오류, 시간 초과, 404 오류는 발생할 수밖에 없습니다. 요청을 tryCatch()로 감싸 오류를 적절히 처리하고, 나중에 다시 시도할 수 있도록 실패한 페이지를 기록하세요.

library(rvest)

safe_scrape <- function(url) {
  tryCatch({
    # page <- read_html(url)
    # data <- extract_data(page)
    # return(data)
    cat(sprintf('Scraping: %s\n', url))
    data.frame(url = url, status = 'ok')
  }, error = function(e) {
    cat(sprintf('FAILED: %s -> %s\n', url, e$message))
    data.frame(url = url, status = 'error')
  })
}

urls <- c('https://example.com/page/1',
          'https://example.com/page/2')
results <- lapply(urls, safe_scrape)
do.call(rbind, results)

스크래핑한 페이지 캐시하기

구문 분석하기 전에 다운로드한 HTML을 디스크에 저장하세요. 그러면 구문 분석에 실패해도 다시 다운로드할 필요가 없습니다. 다시 읽을 때는 네트워크가 아니라 디스크에서 가져오므로 개발 중에 예의 바르고 빠르게 작업할 수 있습니다.

library(rvest)

scrape_cached <- function(url, cache_dir = 'cache') {
  dir.create(cache_dir, showWarnings = FALSE)
  # Create a filename from URL hash
  fname <- file.path(cache_dir,
    paste0(digest::digest(url), '.html'))
  if (file.exists(fname)) {
    cat('Loading from cache\n')
    return(read_html(fname))
  }
  cat('Downloading...\n')
  # page <- read_html(url)
  # xml2::write_html(page, fname)
  # return(page)
  cat('Cached to:', fname, '\n')
}

# Without digest: use URLencode or gsub
url <- 'https://example.com/page/1'
fname <- gsub('[^a-z0-9]', '_', tolower(url))
cat(fname)

빠른 확인

예의 바르고 견고한 여러 페이지 스크래핑에 대한 이해도를 확인해 보세요.

복습: 페이지 매김과 예의 바른 스크래핑

핵심 요점: paste0() 또는 sprintf()로 페이지 URL을 만드세요. purrr::map()으로 여러 URL에 스크래핑을 적용하세요. 요청 사이에는 항상 Sys.sleep()을 추가하세요. robotstxt 패키지로 robots.txt를 확인하세요. html_attr() 또는 session_follow_link()로 '다음' 링크를 동적으로 따라가세요. 다음 링크가 없는지 확인하여 마지막 페이지를 감지하세요. 오류에 견고하게 대응하도록 요청을 tryCatch()로 감싸세요. 개발 중에는 HTML을 디스크에 캐시하세요.

# Multi-page scraping template:
# urls <- paste0(base, 1:n_pages)
# results <- purrr::map(urls, function(url) {
#   Sys.sleep(runif(1, 1, 2))  # polite delay
#   page <- read_html(url)
#   extract_data(page)          # your parser
# })
# df <- dplyr::bind_rows(results)

# Key principles:
# 1. Respect robots.txt
# 2. Add delays (Sys.sleep)
# 3. Set honest User-Agent
# 4. Cache HTML during development
# 5. Handle errors with tryCatch
cat('Polite scraping = sustainable scraping')
무료로 시작

AI 튜터와 함께 R을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
43
레슨
159

자주 묻는 질문

“페이지 매김과 여러 페이지 처리” 강의는 무료인가요?

네 — “페이지 매김과 여러 페이지 처리” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“페이지 매김과 여러 페이지 처리”에서 뭘 배우나요?

페이지가 나뉜 결과를 순회하고 스크랩한 데이터를 하나의 데이터 세트로 결합합니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

R Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“페이지 매김과 여러 페이지 처리” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. HTML 구조와 CSS 선택자
  2. html_element()와 html_text() 기초
  3. 테이블과 링크 스크래핑
  4. 페이지 매김과 여러 페이지 처리
← R Academy(으)로 돌아가기