Ada's Qunatmod answer is to be preferred, but you asked
What code change is required to grab all values in the first table under the graph?
A lot. You want all <li> items in the <ul> below the div[@data-testid="quote-statistics"]. For a cleaner approach, you could target the 12 <fin-streamer> elements
<fin-streamer data-symbol="IVV" data-value="4,559,917" data-trend="none" active="" data-dfield="longFmt" data-field="regularMarketVolume" class="yf-1b7pzha">4,559,917</fin-streamer>
and manually adress the 3 remaining fields and pull their attributes.
In my Code, I use selenider + chromote and then just pull the raw text out of the 15 <li> elements and apply some string splitting.
library(chromote)
library(selenider)
session <- selenider_session("chromote",options = chromote_options(headless = FALSE))
open_url("https://finance.yahoo.com/quote/IVV/")
try(s("button[name='reject']") |> elem_click(), silent = TRUE)
tab <- do.call(rbind,lapply(ss(xpath = '//div[@data-testid="quote-statistics"]//ul//li'), \(x) elem_text(x)))[,1]
res <- sub("52 Week", "FiftyTwo Week", tab)
res <- sub("5Y Monthly", "Five Y Monthly", res)
name <- sub("^(\\D+).*", "\\1", res) |> trimws()
value <- sapply(regmatches(res, regexpr("^(.+?)\\s+(?=[0-9])", res, perl = TRUE), invert = TRUE), \(x) trimws(paste(x[-1], collapse = "")))
si <- data.frame(name = name, value = value)
close_session()
name value
1 Previous Close 638.40
2 Open 639.10
3 Bid 640.02 x 300
4 Ask 640.65 x 400
5 Day's Range 634.59 - 640.73
6 FiftyTwo Week Range 484.00 - 641.74
7 Volume 4,559,917
8 Avg. Volume 5,854,059
9 Net Assets 622.81B
10 NAV 638.12
11 PE Ratio (TTM) 27.42
12 Yield 1.29%
13 YTD Daily Total Return 9.12%
14 Beta (Five Y Monthly) 1.00
15 Expense Ratio (net) 0.03%
Disclaimer: I very much do not recommend this, your IP could be temporarly banned.
A much better approach would be to use the yahoofinancer package (I used kable because it makes the result more clear)
library(yahoofinancer)
s <- Ticker$new('IVV')
yf <- s$get_history(start = today(), interval = '1d')
fields <- c('regular_market_price', 'fifty_two_week_high', 'fifty_two_week_low',
'regular_market_volume', 'exchange_name', 'full_exchange_name',
'previous_close', 'currency', 'exchange_timezone_name', 'symbol')
yf[fields] <- lapply(fields, \(x) s[[x]])
| date |
volume |
high |
low |
open |
close |
adj_close |
regular_market_price |
fifty_two_week_high |
fifty_two_week_low |
regular_market_volume |
exchange_name |
full_exchange_name |
previous_close |
currency |
exchange_timezone_name |
symbol |
| 2025-07-30 20:00:00 |
4559917 |
640.735 |
634.59 |
639.1 |
637.51 |
637.51 |
637.51 |
641.74 |
484 |
4559917 |
PCX |
NYSEArca |
638.4 |
USD |
America/New_York |
IVV |
Or using the code base from {yahoofinanceR} you can write your own function that retrieves the Quote data directly from the API:
# using yahoofinancers function get_meta we can write our own version
# that retrieves the full API response as list
library(jsonlite)
library(httr)
# Source: https://github.com/rsquaredacademy/yahoofinancer/blob/dbad4b14f355ee925650f95d380d0eae52f821ab/R/ticker.R#L433
get_yahoo_symbol_info <- function(sym) {
url <- paste0("https://query2.finance.yahoo.com/v8/finance/chart/", sym)
jsonlite::fromJSON(httr::content(httr::GET(url), "text", encoding = "UTF-8"),
simplifyVector = FALSE)$chart$result[[1]]
}
ivv_info <- get_yahoo_symbol_info("IVV")
stop_for_status(res)is givingUnauthorized (HTTP 401). So @jpsmith and @TimG are probably right (pun not intended).getQuote()function in packagequantmodif you specify the second argumentwhatbased on what the helper functopmyahooQF()helps you select. (There are two named vectors here: one for the JSON request fields, one for for the returned column.) The corresponding Python package also allows you to select fields.