1 + 1[1] 2
Quarto enables you to weave together content and executable code into a finished document. To learn more about Quarto see https://quarto.org.
When you click the Render button a document will be generated that includes both content and the output of embedded code. You can embed code like this:
1 + 1[1] 2
You can add options to executable code like this
[1] 4
The echo: false option disables the printing of code (only output is displayed).
library(rvest)
library(tidyverse)── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.2.1 ✔ readr 2.2.0
✔ forcats 1.0.1 ✔ stringr 1.6.0
✔ ggplot2 4.0.3 ✔ tibble 3.3.1
✔ lubridate 1.9.5 ✔ tidyr 1.3.2
✔ purrr 1.2.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ readr::guess_encoding() masks rvest::guess_encoding()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
url <- "https://books.toscrape.com"
webpage <- read_html(url)
webpage{html_document}
<html lang="en-us" class="no-js">
[1] <head>\n<title>\n All products | Books to Scrape - Sandbox\n</title>\n ...
[2] <body id="default" class="default">\n \n \n \n \n ...
books <- webpage |>
html_elements("article.product_pod")
length(books)[1] 20
The first code loads the packages needed for web scraping and working with data. The second code uses read_html() to read the website. The third code uses a CSS selector to find the books on the page. The result should show 20 books on the first page.
titles <- books |>
html_element("h3 > a") |>
html_attr("title")
head(titles)[1] "A Light in the Attic"
[2] "Tipping the Velvet"
[3] "Soumission"
[4] "Sharp Objects"
[5] "Sapiens: A Brief History of Humankind"
[6] "The Requiem Red"
ratings_raw <- books |>
html_element("p.star-rating") |>
html_attr("class")
ratings <- gsub("star-rating ", "", ratings_raw)
head(ratings)[1] "Three" "One" "One" "Four" "Five" "One"
prices <- books |>
html_element("p.price_color") |>
html_text2()
prices <- as.numeric(gsub("£", "", prices))
head(prices)[1] 51.77 53.74 50.10 47.82 54.23 22.65
These three code chunks extract the book titles, ratings, and prices from the website. The html_element() function selects specific information, and html_attr() extracts attributes. The gsub() function removes unnecessary text from the ratings and the pound symbol from the prices.
stock <- books |>
html_element("p.availability") |>
html_text2() |>
trimws()
head(stock)[1] "In stock" "In stock" "In stock" "In stock" "In stock" "In stock"
images <- books |>
html_element("img.thumbnail") |>
html_attr("src") |>
gsub("../../", "https://books.toscrape.com/", x = _)
head(images)[1] "media/cachttps://books.toscrape.com/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg"
[2] "media/cachttps://books.toscrape.com/0c/260c6ae16bce31c8f8c95daddd9f4a1c.jpg"
[3] "media/cachttps://books.toscrape.com/ef/3eef99c9d9adef34639f510662022830.jpg"
[4] "media/cachttps://books.toscrape.com/51/3251cf3a3412f53f339e42cac2134093.jpg"
[5] "media/cachttps://books.toscrape.com/a5/bea5697f2534a2f86a3ef27b5a8c12a6.jpg"
[6] "media/cachttps://books.toscrape.com/33/68339b4c9bc034267e1da611ab3b34f8.jpg"
test_df <- tibble(
title = titles,
rating = ratings,
price = prices,
stock = stock,
image = images
)
head(test_df)# A tibble: 6 × 5
title rating price stock image
<chr> <chr> <dbl> <chr> <chr>
1 A Light in the Attic Three 51.8 In stock media/cachttps://…
2 Tipping the Velvet One 53.7 In stock media/cachttps://…
3 Soumission One 50.1 In stock media/cachttps://…
4 Sharp Objects Four 47.8 In stock media/cachttps://…
5 Sapiens: A Brief History of Humankind Five 54.2 In stock media/cachttps://…
6 The Requiem Red One 22.6 In stock media/cachttps://…
Here this code collects stock and image information and combine everything into a data frame. To make it easier for us to see what is going on.
all_books <- list()
for (i in 1:50) {
if (i == 1) {
url <- "https://books.toscrape.com"
} else {
url <- paste0("https://books.toscrape.com/catalogue/page-", i, ".html")
}
page <- read_html(url)
books <- html_elements(page, "article.product_pod")
all_books[[i]] <- tibble(
title = html_attr(html_element(books, "h3 > a"), "title"),
rating = gsub("star-rating ", "", html_attr(html_element(books, "p.star-rating"), "class")),
price = html_text2(html_element(books, "p.price_color")),
stock = trimws(html_text2(html_element(books, "p.availability"))),
image = gsub("../../", "https://books.toscrape.com/",
html_attr(html_element(books, "img.thumbnail"), "src"))
)
Sys.sleep(0.5)
cat("Scraped page", i, "\n")
}Scraped page 1
Scraped page 2
Scraped page 3
Scraped page 4
Scraped page 5
Scraped page 6
Scraped page 7
Scraped page 8
Scraped page 9
Scraped page 10
Scraped page 11
Scraped page 12
Scraped page 13
Scraped page 14
Scraped page 15
Scraped page 16
Scraped page 17
Scraped page 18
Scraped page 19
Scraped page 20
Scraped page 21
Scraped page 22
Scraped page 23
Scraped page 24
Scraped page 25
Scraped page 26
Scraped page 27
Scraped page 28
Scraped page 29
Scraped page 30
Scraped page 31
Scraped page 32
Scraped page 33
Scraped page 34
Scraped page 35
Scraped page 36
Scraped page 37
Scraped page 38
Scraped page 39
Scraped page 40
Scraped page 41
Scraped page 42
Scraped page 43
Scraped page 44
Scraped page 45
Scraped page 46
Scraped page 47
Scraped page 48
Scraped page 49
Scraped page 50
full_df <- bind_rows(all_books)
nrow(full_df)[1] 1000
And this code goes through all 50 pages and combines the book information into one dataset.
full_df$price <- as.numeric(gsub("£", "", full_df$price))
head(full_df)# A tibble: 6 × 5
title rating price stock image
<chr> <chr> <dbl> <chr> <chr>
1 A Light in the Attic Three 51.8 In stock media/cachttps://…
2 Tipping the Velvet One 53.7 In stock media/cachttps://…
3 Soumission One 50.1 In stock media/cachttps://…
4 Sharp Objects Four 47.8 In stock media/cachttps://…
5 Sapiens: A Brief History of Humankind Five 54.2 In stock media/cachttps://…
6 The Requiem Red One 22.6 In stock media/cachttps://…
rating_map <- c(One=1, Two=2, Three=3, Four=4, Five=5)
full_df1 <- full_df |>
mutate(rating_num = rating_map[rating])
table1 <- full_df1 |>
count(rating, sort = TRUE)
table1# A tibble: 5 × 2
rating n
<chr> <int>
1 One 226
2 Three 203
3 Five 196
4 Two 196
5 Four 179
full_df1 |>
group_by(rating) |>
summarise(avg_price = mean(price, na.rm = TRUE)) |>
arrange(desc(avg_price))# A tibble: 5 × 2
rating avg_price
<chr> <dbl>
1 Four 36.1
2 Five 35.4
3 Two 34.8
4 Three 34.7
5 One 34.6
And then this code goes and converts prices and ratings into numbers, count books by rating, and calculate average prices.