0Pricing
R Academy · レッスン

ページネーションと複数ページの処理

ページ分割された結果をループ処理し、スクレイピングしたデータを1つのデータセットにまとめます。

「ページネーションと複数ページの処理」はCoddyKit上の無料R Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。

ページネーションを理解する

多くのWebサイトでは、コンテンツを複数のページに分割しています。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()のほうがゼロ埋めや書式設定を細かく制御できます。サイトの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()と組み合わせると、1つのデータフレームにまとめられます。

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ヘッダーを設定し、スクレイパーを明示してください。サーバーはこれを使ってボットを識別します。適切な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()は、Cookieと履歴を保持する状態付きのブラウジングセッションを作成します。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')

よくある質問

「ページネーションと複数ページの処理」レッスンは無料ですか?

はい。「ページネーションと複数ページの処理」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「ページネーションと複数ページの処理」で何を学びますか?

ページ分割された結果をループ処理し、スクレイピングしたデータを1つのデータセットにまとめます。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応の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に戻る