polite R package

repository·develop·Indexed 43 days ago

https://github.com/dmi3kno/polite

An R package for responsible web harvesting that ensures adherence to robots.txt policies, implements rate limiting, and manages HTTP sessions. It features a workflow using bow(), scrape(), and nod() to seek permission and retrieve data safely, as well as a politely() adverb to wrap existing web-accessing functions and a use_manners() template for package development.

Tokens
2.2K
Snippets
10
Records
10
Agent score
6%

What's inside polite

  1. How bow, scrape, and nod work together

    develop

    The polite package follows a "bow and scrape" workflow to ensure responsible web harvesting etiquette. The workflow is built on three pillars: seeking permission, taking slowly and never asking twice.

    1. bow(): Used to introduce the client to the host. It inquires against the host's robots.txt file to ask for permission to scrape. This establishes a polite session.
    2. scrape(): The main function used to retrieve data from the remote server using the established session. It automatically handles rate limiting and respects the rules negotiated during the bow step.
    3. nod(): Used to adjust the session's URL. If you need to move to a new path, nod updates the session's URL while ensuring the new location is also negotiated against robots.txt. Once a connection is established via bow, you do not need to bow again for the same host, only nod to change paths.

    Behind the scenes, polite manages robots.txt caching, user agent strings, rate limiting, and response caching.

    session <- bow("https://example.com")
    # Use the session to scrape
    result <- scrape(session)
    # Move to a new path within the same host
    session <- nod(session, "https://example.com/new-path")
    result <- scrape(session)
  2. Use the politely() adverb as a web-accessing backend

    develop

    The politely() function is an 'adverb' (similar to purrr adverbs) that can wrap any web-accessing function to make it follow the four pillars of polite scraping: Introduce Yourself, Seek Permission, Take Slowly and Never Ask Twice.

    To use it, wrap your existing function (like httr::GET or httr::POST) with politely() to create a new function. This new function will automatically handle:

    • Providing a human-readable user-agent string.
    • Consulting robots.txt for permissions.
    • Respecting rate limits (crawl delays).
    • Caching results (memoization).

    Requirement: The wrapped function must accept a url argument (or have url as its first argument).

    # Example: Wrapping httr::GET
    polite_GET <- politely(httr::GET, verbose=TRUE)
    
    # Use the new function instead of the original
    res <- polite_GET("http://colormind.io/list")
  3. Use the polite template for package development

    develop

    If you are developing an R package that accesses the web, you can use polite::use_manners() to bootstrap a polite scraping workflow.

    When called within your package or analysis directory, it creates R/polite-scrape.R containing template functions that act as drop-in replacements for common web-accessing tools. These functions use a polite_ prefix and are designed to look and feel like the original functions while adhering to polite scraping principles.

    Common replacements include:

    • read_html() $\rightarrow$ polite_read_html()
    • download.file() $\rightarrow$ polite_download_file()
    polite::use_manners()
  4. Install the polite package

    develop

    You can install the stable version of polite from CRAN or the development version from GitHub.

    From CRAN:

    install.packages("polite")

    From GitHub (Development version):

    install.packages("remotes")
    remotes::install_github("dmi3kno/polite")
    install.packages("polite")
  5. Install polite via CRAN or GitHub

    develop

    You can install the stable version of polite from CRAN:

    install.packages("polite")

    To install the development version from GitHub, use the remotes package:

    install.packages("remotes")
    remotes::install_github("dmi3kno/polite")
    install.packages("polite")
  6. Basic usage: Scrape a page with query parameters

    develop

    To perform a simple scrape, use bow() to create a session and scrape() to retrieve the page content. You can pass a query list to scrape() to handle URL parameters.

    library(polite)
    library(rvest)
    
    session <- bow("https://www.cheese.com/by_type", force = TRUE)
    result <- scrape(session, query=list(t="semi-soft", per_page=100)) %>%
      html_node("#main-body") %>%
      html_nodes("h3") %>%
      html_text()
    head(result)
    library(polite)
    library(rvest)
    
    session <- bow("https://www.cheese.com/by_type", force = TRUE)
    result <- scrape(session, query=list(t="semi-soft", per_page=100)) %>%
      html_node("#main-body") %>%
      html_nodes("h3") %>%
      html_text()
    head(result)
  7. Advanced usage: Crawling multiple pages with nod()

    develop

    When navigating through a site (e.g., following "Older posts" links), use nod() to update the session to the new URL within a loop. This ensures every new URL is checked against robots.txt.

    library(polite)
    library(rvest)
    
    hrbrmstr_posts <- data.frame()
    url <- "https://rud.is/b/"
    session <- bow(url)
    
    while(!is.na(url)){
      message("Scraping ", url)
      
      # Update session to the new URL and scrape
      current_page <- nod(session, url) %>%
        scrape(verbose=TRUE)
      
      # Extract data
      hrbrmstr_posts <- current_page %>%
        html_nodes(".entry-title a") %>%
        polite::html_attrs_dfr() %>%
        rbind(hrbrmstr_posts)
      
      # Find the next URL
      url <- current_page %>%
        html_node(".nav-previous a") %>%
        html_attr("href")
    }
    
    tibble::as_tibble(hrbrmstr_posts)
    library(polite)
    library(rvest)
    
    hrbrmstr_posts <- data.frame()
    url <- "https://rud.is/b/"
    session <- bow(url)
    
    while(!is.na(url)){
      message("Scraping ", url)
      current_page <- nod(session, url) %>%
        scrape(verbose=TRUE)
      hrbrmstr_posts <- current_page %>%
        html_nodes(".entry-title a") %>%
        polite::html_attrs_dfr() %>%
        rbind(hrbrmstr_posts)
      url <- current_page %>%
        html_node(".nav-previous a") %>%
        html_attr("href")
    }
    
    tibble::as_tibble(hrbrmstr_posts)
  8. Configure robots.txt validation in politely()

    develop

    By default, functions created via politely() will check robots.txt to ensure the requested URL is allowed. If you need to bypass this check (for example, if an API endpoint is disallowed in robots.txt but legal to access for small requests), you can set the robots argument to FALSE when creating the polite function.

    library(polite)
    library(httr)
    
    # Create a GET function that ignores robots.txt
    polite_GET_nrt <- politely(GET, verbose=TRUE, robots = FALSE)
    
    # Use it to access a disallowed endpoint
    beatles_lst <- polite_GET_nrt("https://musicbrainz.org/ws/2/artist/",
                       query=list(query="Beatles", limit=10),
                       httr::accept("application/json")) %>%
      httr::content(type = "application/json")
  9. Initialize a polite package template with use_manners()

    develop
    Package developers can use polite::use_manners() to set up a polite scraping infrastructure. When run within an analysis or package directory, it creates R/polite-scrape.R containing template functions that are drop-in replacements for common web-accessing tools. These functions use the polite_ prefix and are designed to be easily swapped into existing code.
    polite::use_manners()
  10. Use the politely() adverb to wrap existing functions

    develop

    The politely() function is an adverb that can make any web-accessing function (like those from httr) follow polite principles. It automatically handles user-agent strings, robots.txt consultation, rate limiting, and memoization.

    To use it, wrap the target function and then call the resulting function. The target function must have a url argument (or the first argument must be a URL).

    Note: You can disable robots.txt validation by passing robots = FALSE to the adverb.

    library(polite)
    library(httr)
    
    # Wrap httr::GET to create a polite version
    polite_GET <- politely(httr::GET, verbose=TRUE)
    
    # Use the new function just like the original
    res <- polite_GET("http://colormind.io/list")
    
    # Example: Disabling robots.txt check for specific APIs
    polite_GET_nrt <- politely(GET, verbose=TRUE, robots = FALSE)
    res <- polite_GET_nrt("https://musicbrainz.org/ws/2/artist/", query=list(query="Beatles"))