Scrape Daily Daylight (R)
gist
An R script for retrieving sunrise-sunset times and calculating daily daylight duration from the Thai Astronomical Society
This is R code used to retrieve sunrise and sunset times from the Thai Astronomical Society website (thaiastro.nectec.or.th), then use that data to calculate the length of Daylight Hours per day for a given province.
This dataset is very useful for research related to the environmental impact of light on plant growth, or for studies of animal behavior.
📦 Required Libraries
Before using this code, install and load the following libraries:
library(rvest) # For web scraping, reading HTML structure, and extracting data
library(dplyr) # For manipulating and cleaning data frames
library(stringr) # For string manipulation
library(lubridate) # For handling and calculating dates and times
library(purrr) # For looping through each month's table (functional programming)💻 The get_daily_daylight Function
Full code for the data retrieval function
get_daily_daylight <- function(year, province_name) {
# 1. Handle the year number
year_ce <- ifelse(year > 2500, year - 543, year)
index_url <- paste0("https://thaiastro.nectec.or.th/skyevnt/sunmoon/", year_ce, "/")
# 2. Find the link for the target province
index_page <- tryCatch(
read_html(index_url, encoding = "UTF-8"),
error = function(e) { stop(paste("Unable to access the index data for year", year_ce)) }
)
links <- index_page %>% html_nodes("a")
prov_df <- data.frame(
province = html_text(links) %>% str_trim(),
href = html_attr(links, "href"),
stringsAsFactors = FALSE
)
matched_row <- prov_df %>% filter(str_detect(province, province_name))
if (nrow(matched_row) == 0) stop(paste("No data found for province:", province_name))
# 3. Retrieve data from that province's page
target_url <- paste0("https://thaiastro.nectec.or.th/skyevnt/sunmoon/", year_ce, "/", matched_row$href[1])
target_page <- read_html(target_url, encoding = "UTF-8")
# Extract "all" tables on that page (this site has 1 table per month)
tables <- target_page %>% html_table(fill = TRUE)
# Keep only tables with 11 columns (the standard structure of this site's schedule tables)
valid_tables <- keep(tables, ~ ncol(.x) == 11)
if (length(valid_tables) == 0) stop("No table with the correct structure (11 columns) found on this page")
# 4. Bind all 12 months' tables together (bind rows)
raw_df <- map_df(valid_tables, function(tbl) {
# Temporarily rename columns so they can be bound together
colnames(tbl) <- paste0("V", 1:11)
# Convert every column to character to prevent data type mismatch errors
mutate_all(tbl, as.character)
})
# Rename columns according to the actual structure
colnames(raw_df) <- c("DateStr", "Weekday",
"Sunrise", "Sunrise_Azimuth",
"Sunset", "Sunset_Azimuth",
"Moon_Illum",
"Moonrise", "Moonrise_Azimuth",
"Moonset", "Moonset_Azimuth")
# 5. Clean the data and calculate the daylight duration
final_data <- raw_df %>%
# Keep only rows that are actual time data (identified by the ":" character)
filter(str_detect(Sunrise, ":") & str_detect(Sunset, ":")) %>%
mutate(
Year = year_ce,
Province = province_name,
# Split the date and month apart (e.g., "1 ม.ค." -> Day=1, Month_Th="ม.ค.")
Day = as.integer(str_extract(DateStr, "^\\d+")),
Month_Th = str_replace(DateStr, "^\\d+\\s*", ""),
# Convert the "HH:MM" text into a Period
rise_time = hm(Sunrise),
set_time = hm(Sunset),
# Calculate daylight duration in hours (decimal)
Daylight_Hours = round(time_length(set_time - rise_time, unit = "hour"), 2)
) %>%
# Arrange the columns needed for further use
select(Year, Province, Month_Th, Day, Weekday, Sunrise, Sunset, Daylight_Hours)
return(final_data)
}💡 Real-World Usage Example
See an example of using the function to retrieve daily daylight duration in 2024 for various locations
# Retrieve data for Phatthalung province in 2024
phatthalung_daylight <- get_daily_daylight(2024, "พัทลุง")
# Check the first 5 rows of the data table
head(phatthalung_daylight)
# Retrieve data for Bangkok in 2025
bkk_daylight <- get_daily_daylight(2025, "กรุงเทพมหานคร")
head(bkk_daylight)