Working with HTML and JSON

Author

Ummay Rukiya

Published

October 18, 2026

Overview

For this assignment, I used three books about space and astronomy to practice working with HTML and JSON. I chose this subject because I enjoy learning about space.

The books are Cosmos, Pale Blue Dot, and Welcome to the Universe: An Astrophysical Tour. The third book has three authors, which allows me to explore how multiple authors can be represented differently in the two formats.

For each book, I recorded the title, authors, publication year, publisher, and ISBN. I will manually create an HTML table and a JSON file containing the same information, read both files from GitHub into separate R data frames, and compare the results.

Book Information Sources

I checked the book details using the publishers’ websites:

I used the publication year and ISBN for each selected edition rather than mixing details from different editions. The selected editions are the 2013 paperback of Cosmos, the 1997 paperback of Pale Blue Dot, and the 2016 hardcover of Welcome to the Universe.

The publisher lists Carl Sagan and Ann Druyan as authors of the selected Pale Blue Dot edition, so I will include both names in my files.

Loading Required Packages

I used rvest to read the HTML table and jsonlite to read the JSON file. I used knitr to display the results as tables.

Code
library(rvest)
library(jsonlite)
library(knitr)

Loading the HTML Data

The HTML file contains one table with three books and five columns. I read the table from its public GitHub URL and stored it as a data frame. I disabled automatic type conversion so the ISBN values would remain text.

Code
html_url <- paste0(
  "https://raw.githubusercontent.com/UR-71/",
  "DATA607-HTML-JSON/main/books.html"
)

books_html <- read_html(html_url) |>
  html_element("table") |>
  html_table(convert = FALSE) |>
  as.data.frame()

kable(
  books_html,
  caption = "Book Data Imported from HTML"
)
Book Data Imported from HTML
title authors publication_year publisher isbn
Cosmos Carl Sagan 2013 Ballantine Books 9780345539434
Pale Blue Dot Carl Sagan; Ann Druyan 1997 Ballantine Books 9780345376596
Welcome to the Universe: An Astrophysical Tour Neil deGrasse Tyson; Michael A. Strauss; J. Richard Gott 2016 Princeton University Press 9780691157245
Code
dim(books_html)
[1] 3 5

Loading the JSON Data

I read the JSON file from its public GitHub URL into a separate data frame. The authors are stored as arrays in JSON, so they are imported as a list column rather than the single text column used in the HTML table.

Code
json_url <- paste0(
  "https://raw.githubusercontent.com/UR-71/",
  "DATA607-HTML-JSON/main/books.json"
)

books_json <- fromJSON(json_url)

dim(books_json)
[1] 3 5
Code
str(books_json)
'data.frame':   3 obs. of  5 variables:
 $ title           : chr  "Cosmos" "Pale Blue Dot" "Welcome to the Universe: An Astrophysical Tour"
 $ authors         :List of 3
  ..$ : chr "Carl Sagan"
  ..$ : chr  "Carl Sagan" "Ann Druyan"
  ..$ : chr  "Neil deGrasse Tyson" "Michael A. Strauss" "J. Richard Gott"
 $ publication_year: int  2013 1997 2016
 $ publisher       : chr  "Ballantine Books" "Ballantine Books" "Princeton University Press"
 $ isbn            : chr  "9780345539434" "9780345376596" "9780691157245"

Comparing the Original Imports

Both data frames contain the same book information, but they store some values differently. I compared them before making any changes to their structures.

Code
identical(books_html, books_json)
[1] FALSE
Code
all.equal(books_html, books_json)
[1] "Component \"authors\": Modes: character, list"                          
[2] "Component \"authors\": target is character, current is list"            
[3] "Component \"publication_year\": Modes: character, numeric"              
[4] "Component \"publication_year\": target is character, current is numeric"

Making the Structures Consistent

I converted the HTML publication years to integers and combined each JSON author array into one text value, separating authors with semicolons. I also matched the column order, sorted both data frames by title, and reset their row names before comparing them again.

Code
books_html_clean <- books_html
books_json_clean <- books_json

books_html_clean$publication_year <- as.integer(
  books_html_clean$publication_year
)

books_json_clean$authors <- vapply(
  books_json_clean$authors,
  function(authors) paste(authors, collapse = "; "),
  character(1)
)

books_json_clean <- books_json_clean[
  , names(books_html_clean),
  drop = FALSE
]

books_html_clean <- books_html_clean[
  order(books_html_clean$title),
  ,
  drop = FALSE
]

books_json_clean <- books_json_clean[
  order(books_json_clean$title),
  ,
  drop = FALSE
]

rownames(books_html_clean) <- NULL
rownames(books_json_clean) <- NULL

identical(books_html_clean, books_json_clean)
[1] TRUE
Code
all.equal(books_html_clean, books_json_clean)
[1] TRUE
Code
kable(
  books_json_clean,
  caption = "Book Data After Standardizing the Structures"
)
Book Data After Standardizing the Structures
title authors publication_year publisher isbn
Cosmos Carl Sagan 2013 Ballantine Books 9780345539434
Pale Blue Dot Carl Sagan; Ann Druyan 1997 Ballantine Books 9780345376596
Welcome to the Universe: An Astrophysical Tour Neil deGrasse Tyson; Michael A. Strauss; J. Richard Gott 2016 Princeton University Press 9780691157245

Conclusion

The HTML and JSON files contained the same information about three astronomy books, but the data frames were not identical immediately after importing them. HTML stored the authors and publication years as text, while JSON stored the authors as arrays and the years as integers.

After combining the JSON author arrays into text and making the data types and ordering consistent, both identical() and all.equal() returned TRUE. This confirmed that the two data frames matched.

This exercise helped me understand that the same information can have different structures depending on its file format. Checking those structures is important before comparing or combining imported data.