#Get data
url <- "https://www.goodreads.com/list/show/1.Best_Books_Ever"Chrome CSS Selectors and Webpage Inspect
Web Scraping in Class Activity
Load libraries
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()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(rvest)
Attaching package: 'rvest'
The following object is masked from 'package:readr':
guess_encoding
library(jsonlite)
Attaching package: 'jsonlite'
The following object is masked from 'package:purrr':
flatten
library(robotstxt)Extracting Information exp. 1
#Extract Rank, Title, Author name, Score for each book
# read HTML
page <- read_html(url)
#check permission
paths_allowed(url)
www.goodreads.com
[1] TRUE
#extract fields
rank <- page %>%
#find the element's tag with the Chrome Selector Gadget
html_elements(".number") %>%
html_text(trim = TRUE) # get text, remove spaces
title <- page %>%
html_elements(".bookTitle span") %>%
html_text(trim = TRUE)
author <- page %>%
html_elements(".authorName span") %>%
html_text(trim = TRUE)
score <- page %>%
html_elements(".minirating") %>%
html_text(trim = TRUE)
#make data frame
bestBooks <- tibble(
rank = rank,
title = title,
author = author,
score = score
)
bestBooks# A tibble: 100 × 4
rank title author score
<chr> <chr> <chr> <chr>
1 1 The Hunger Games (The Hunger Games, #1) Suzan… 4.36…
2 2 Pride and Prejudice Jane … 4.30…
3 3 To Kill a Mockingbird Harpe… 4.26…
4 4 Harry Potter and the Order of the Phoenix (Harry Potter, … J.K. … 4.50…
5 5 The Book Thief Marku… 4.39…
6 6 Twilight (The Twilight Saga, #1) Steph… 3.68…
7 7 Animal Farm Georg… 4.03…
8 8 J.R.R. Tolkien 4-Book Boxed Set: The Hobbit and The Lord … J.R.R… 4.62…
9 9 The Chronicles of Narnia (The Chronicles of Narnia, #1-7) C.S. … 4.28…
10 10 The Fault in Our Stars John … 4.12…
# ℹ 90 more rows
#save to CSV (Excel-ready)
write_csv(bestBooks, "Best_Books_Ever") #for analysis!
getwd() #to check where it was saved[1] "C:/Users/Konci/OneDrive/Documents/DATA110_Fall2026"
Extracting Information exp. 2
#Extract obervations for Ireland, Switzerland, Iceland, and Norway
url <- "https://en.wikipedia.org/wiki/List_of_countries_by_GDP_%28nominal%29_per_capita"
h <- read_html(url)
#check permissions
paths_allowed(url) #are we allowed to script?
en.wikipedia.org
[1] TRUE
class(h) #what is the class of this data?[1] "xml_document" "xml_node"
#View the tables on the webpage, find the correct table using inspect
tab <- h |> html_nodes("table")
tab{xml_nodeset (6)}
[1] <table width="100%h" id="mwDg"><tbody id="mwDw"><tr id="mwEA">\n<td valig ...
[2] <table class="wikitable sortable static-row-numbers sort-under-center" st ...
[3] <table class="nowraplinks mw-collapsible uncollapsed navbox-inner" style= ...
[4] <table class="nowraplinks hlist mw-collapsible autocollapse navbox-inner" ...
[5] <table class="nowraplinks navbox-subgroup" style="border-spacing:0"><tbod ...
[6] <table class="nowraplinks mw-collapsible autocollapse navbox-inner" style ...
#View the names
html_table(tab[[2]]) |> names()[1] "Country/Territory" "IMF(2026)[b][5]"
[3] "World Bank(2025)[6]" "United Nations(2023-24)[7]"
#Extract the table
tab2 <- tab[[2]] %>% html_table
tab2# A tibble: 223 × 4
`Country/Territory` `IMF(2026)[b][5]` `World Bank(2025)[6]`
<chr> <chr> <chr>
1 Monaco —.mw-parser-output .sr-only{border… 288,002 (2024)
2 Liechtenstein 230,417 220,167 (2024)
3 Luxembourg 155,733 147,252
4 Bermuda —N/a 142,250 (2024)
5 Ireland 127,837 131,593
6 Switzerland 124,478 114,769
7 Norway 115,188 94,594
8 Singapore 109,117 98,814
9 Iceland 107,464 98,324
10 Cayman Islands —N/a 104,293 (2024)
# ℹ 213 more rows
# ℹ 1 more variable: `United Nations(2023-24)[7]` <chr>
#class of table object
class(tab2) #data.frame[1] "tbl_df" "tbl" "data.frame"
#add column names
tab2 <- tab2 |> setNames(c("country","IMF","world_bank","united_nations"))
head(tab2)# A tibble: 6 × 4
country IMF world_bank united_nations
<chr> <chr> <chr> <chr>
1 Monaco —.mw-parser-output .sr-only{border:0;… 288,002 (… 288,002
2 Liechtenstein 230,417 220,167 (… 216,392
3 Luxembourg 155,733 147,252 138,596
4 Bermuda —N/a 142,250 (… 136,766
5 Ireland 127,837 131,593 109,874
6 Switzerland 124,478 114,769 104,973
#Observe only Ireland,Switzerland, Iceland, and Norway
tab2 <- filter(tab2, country %in% c("Ireland","Switzerland","Iceland","Norway"))
head(tab2)# A tibble: 4 × 4
country IMF world_bank united_nations
<chr> <chr> <chr> <chr>
1 Ireland 127,837 131,593 109,874
2 Switzerland 124,478 114,769 104,973
3 Norway 115,188 94,594 86,717
4 Iceland 107,464 98,324 84,450
library(ggplot2)
#basic chart template
gdpChart <- ggplot(tab2, aes(x = country, y = world_bank)) +
labs(title = "GDP by Country") +
ylab("GDP (in USD)") +
xlab("Country")
theme_minimal(base_size = 14)<theme> List of 144
$ line : <ggplot2::element_line>
..@ colour : chr "black"
..@ linewidth : num 0.636
..@ linetype : num 1
..@ lineend : chr "butt"
..@ linejoin : chr "round"
..@ arrow : logi FALSE
..@ arrow.fill : chr "black"
..@ inherit.blank: logi TRUE
$ rect : <ggplot2::element_rect>
..@ fill : chr "white"
..@ colour : chr "black"
..@ linewidth : num 0.636
..@ linetype : num 1
..@ linejoin : chr "round"
..@ inherit.blank: logi TRUE
$ text : <ggplot2::element_text>
..@ family : chr ""
..@ face : chr "plain"
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : chr "black"
..@ size : num 14
..@ hjust : num 0.5
..@ vjust : num 0.5
..@ angle : num 0
..@ lineheight : num 0.9
..@ margin : <ggplot2::margin> num [1:4] 0 0 0 0
..@ debug : logi FALSE
..@ inherit.blank: logi TRUE
$ title : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : NULL
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ point : <ggplot2::element_point>
..@ colour : chr "black"
..@ shape : num 19
..@ size : num 1.91
..@ fill : chr "white"
..@ stroke : num 0.636
..@ inherit.blank: logi TRUE
$ polygon : <ggplot2::element_polygon>
..@ fill : chr "white"
..@ colour : chr "black"
..@ linewidth : num 0.636
..@ linetype : num 1
..@ linejoin : chr "round"
..@ inherit.blank: logi TRUE
$ geom : <ggplot2::element_geom>
..@ ink : chr "black"
..@ paper : chr "white"
..@ accent : chr "#3366FF"
..@ linewidth : num 0.636
..@ borderwidth: num 0.636
..@ linetype : int 1
..@ bordertype : int 1
..@ family : chr ""
..@ fontsize : num 4.92
..@ pointsize : num 1.91
..@ pointshape : num 19
..@ colour : NULL
..@ fill : NULL
$ spacing : 'simpleUnit' num 7points
..- attr(*, "unit")= int 8
$ margins : <ggplot2::margin> num [1:4] 7 7 7 7
$ aspect.ratio : NULL
$ axis.title : NULL
$ axis.title.x : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : num 1
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 3.5 0 0 0
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.title.x.top : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : num 0
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 0 0 3.5 0
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.title.x.bottom : NULL
$ axis.title.y : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : num 1
..@ angle : num 90
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 0 3.5 0 0
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.title.y.left : NULL
$ axis.title.y.right : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : num 1
..@ angle : num -90
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 0 0 0 3.5
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.text : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : chr "#4D4D4DFF"
..@ size : 'rel' num 0.8
..@ hjust : NULL
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : NULL
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.text.x : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : num 1
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 2.8 0 0 0
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.text.x.top : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 0 0 6.3 0
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.text.x.bottom : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 6.3 0 0 0
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.text.y : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : num 1
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 0 2.8 0 0
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.text.y.left : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 0 6.3 0 0
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.text.y.right : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : NULL
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 0 0 0 6.3
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.text.theta : NULL
$ axis.text.r : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : num 0.5
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : <ggplot2::margin> num [1:4] 0 2.8 0 2.8
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ axis.ticks : <ggplot2::element_blank>
$ axis.ticks.x : NULL
$ axis.ticks.x.top : NULL
$ axis.ticks.x.bottom : NULL
$ axis.ticks.y : NULL
$ axis.ticks.y.left : NULL
$ axis.ticks.y.right : NULL
$ axis.ticks.theta : NULL
$ axis.ticks.r : NULL
$ axis.minor.ticks.x.top : NULL
$ axis.minor.ticks.x.bottom : NULL
$ axis.minor.ticks.y.left : NULL
$ axis.minor.ticks.y.right : NULL
$ axis.minor.ticks.theta : NULL
$ axis.minor.ticks.r : NULL
$ axis.ticks.length : 'rel' num 0.5
$ axis.ticks.length.x : NULL
$ axis.ticks.length.x.top : NULL
$ axis.ticks.length.x.bottom : NULL
$ axis.ticks.length.y : NULL
$ axis.ticks.length.y.left : NULL
$ axis.ticks.length.y.right : NULL
$ axis.ticks.length.theta : NULL
$ axis.ticks.length.r : NULL
$ axis.minor.ticks.length : 'rel' num 0.75
$ axis.minor.ticks.length.x : NULL
$ axis.minor.ticks.length.x.top : NULL
$ axis.minor.ticks.length.x.bottom: NULL
$ axis.minor.ticks.length.y : NULL
$ axis.minor.ticks.length.y.left : NULL
$ axis.minor.ticks.length.y.right : NULL
$ axis.minor.ticks.length.theta : NULL
$ axis.minor.ticks.length.r : NULL
$ axis.line : <ggplot2::element_blank>
$ axis.line.x : NULL
$ axis.line.x.top : NULL
$ axis.line.x.bottom : NULL
$ axis.line.y : NULL
$ axis.line.y.left : NULL
$ axis.line.y.right : NULL
$ axis.line.theta : NULL
$ axis.line.r : NULL
$ legend.background : <ggplot2::element_blank>
$ legend.margin : NULL
$ legend.spacing : 'rel' num 2
$ legend.spacing.x : NULL
$ legend.spacing.y : NULL
$ legend.key : <ggplot2::element_blank>
$ legend.key.size : 'simpleUnit' num 1.2lines
..- attr(*, "unit")= int 3
$ legend.key.height : NULL
$ legend.key.width : NULL
$ legend.key.spacing : NULL
$ legend.key.spacing.x : NULL
$ legend.key.spacing.y : NULL
$ legend.key.justification : NULL
$ legend.frame : NULL
$ legend.ticks : NULL
$ legend.ticks.length : 'rel' num 0.2
$ legend.axis.line : NULL
$ legend.text : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : 'rel' num 0.8
..@ hjust : NULL
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : NULL
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ legend.text.position : NULL
$ legend.title : <ggplot2::element_text>
..@ family : NULL
..@ face : NULL
..@ italic : chr NA
..@ fontweight : num NA
..@ fontwidth : num NA
..@ colour : NULL
..@ size : NULL
..@ hjust : num 0
..@ vjust : NULL
..@ angle : NULL
..@ lineheight : NULL
..@ margin : NULL
..@ debug : NULL
..@ inherit.blank: logi TRUE
$ legend.title.position : NULL
$ legend.position : chr "right"
$ legend.position.inside : NULL
$ legend.direction : NULL
$ legend.byrow : NULL
$ legend.justification : chr "center"
$ legend.justification.top : NULL
$ legend.justification.bottom : NULL
$ legend.justification.left : NULL
$ legend.justification.right : NULL
$ legend.justification.inside : NULL
[list output truncated]
@ complete: logi TRUE
@ validate: logi TRUE
gdpChart#Bar plot comparing nominal GDP per capita (in USD) for Iceland, Ireland, Norway, and Switzerland
gdpChart +
geom_bar(stat = "identity", color = "white", aes(fill = country))