Search code examples
rrvest

Rvest returns only some html_nodes


I'm trying to scrape the gbarbosa page, but it only returns 8 nodes, when the total number of products is 16 in page one. Any suggestions?

library(rvest)

url <- "https://www.gbarbosa.com.br/caes/caes?_q=C%C3%A3es&map=ft,categoria&page=1"

sku <-  
 read_html(url) |>
 html_nodes(".gbarbosa-gbarbosa-components-0-x-ProductName.false") |>
 html_text(trim = TRUE)

gives

> sku
[1] "Ração p/ Cães Pedigree Adulto 7+ Carne Sachê 100g"                                                
[2] "Ração p/ Cães Pedigree Adulto Raças Pequenas Carne 100g"                                          
[3] "Ração p/ Cães Pedigree Adulto Raças Pequenas Frango 100g"                                         
[4] "Alimento p/ Cães Balance Adultos Premium Especial Cordeiro e Vegetais ao Molho Sachê 85g"         
[5] "Alimento p/ Cães Pedigree Nutrição Essencial Adultos 9+ Carne ao Leite Pacote 10.1kg Grátis 1.1kg"
[6] "Tapete Higiênico Jumbo s/ Fragrância 80x60cm c/ 7 Unid"                                           
[7] "Ração Kynus p/ Cães Adultos Carne Pacote 15Kg"                                                    
[8] "Petisco Dguitos Bifinho de Carne Cães Adultos e Filhotes 65g"

Solution

  • Item data for that page is embedded in script elements as ldjson, you can extract it with rvest, parse with jsonlite as a normal JSON and rectangle resulting list into a frame (tidyr comes with a few handy tools for that):

    library(rvest)
    library(jsonlite)
    library(dplyr, warn.conflicts = FALSE)
    library(tidyr)
    
    url_ <- "https://www.gbarbosa.com.br/caes/caes?_q=C%C3%A3es&map=ft,categoria&page=1" 
    
    ldjson <- 
      read_html(url_) |> 
      html_element(".render-container script[type='application/ld+json']") |> 
      html_text() |> 
      parse_json()
    
    # details of the 1st item:
    lobstr::tree(ldjson[["itemListElement"]][[1]])
    #> <list>
    #> ├─@type: "ListItem"
    #> ├─position: 1
    #> └─item: <list>
    #>   ├─@context: "https://schema.org/"
    #>   ├─@type: "Product"
    #>   ├─@id: "https://www.gbarbosa.com.br/raca..."
    #>   ├─name: "Ração p/ Cães Pedigree Adulto 7+..."
    #>   ├─brand: <list>
    #>   │ ├─@type: "Brand"
    #>   │ └─name: "Pedigree"
    #>   ├─image: "https://gbarbosa.vtexassets.com/..."
    #>   ├─description: ""
    #>   ├─mpn: "1634520"
    #>   ├─sku: "7922"
    #>   ├─offers: <list>
    #>   │ ├─@type: "AggregateOffer"
    #>   │ ├─lowPrice: 3.39
    #>   │ ├─highPrice: 3.39
    #>   │ ├─priceCurrency: "BRL"
    #>   │ ├─offers: <list>
    #>   │ │ └─<list>
    #>   │ │   ├─@type: "Offer"
    #>   │ │   ├─price: 3.39
    #>   │ │   ├─priceCurrency: "BRL"
    #>   │ │   ├─availability: "http://schema.org/InStock"
    #>   │ │   ├─sku: "7922"
    #>   │ │   ├─itemCondition: "http://schema.org/NewCondition"
    #>   │ │   ├─priceValidUntil: "2026-01-18T11:20:49Z"
    #>   │ │   └─seller: <list>
    #>   │ │     ├─@type: "Organization"
    #>   │ │     └─name: "www.gbarbosa.com.br"
    #>   │ └─offerCount: 1
    #>   └─gtin: "7896029014981"
    
    
    ldjson$itemListElement |> 
      tibble(items = _) |> 
      hoist(items, 
            brand = list("item", "brand", "name"),
            name  = list("item", "name"),
            sku   = list("item", "sku"),
            lowPrice  = list("item", "offers", "lowPrice"),
            highPrice = list("item", "offers", "highPrice")
      ) |> 
      select(-items)
    

    Results:

    #> # A tibble: 16 × 5
    #>    brand    name                                        sku   lowPrice highPrice
    #>    <chr>    <chr>                                       <chr>    <dbl>     <dbl>
    #>  1 Pedigree Ração p/ Cães Pedigree Adulto 7+ Carne Sac… 7922      3.39      3.39
    #>  2 Pedigree Ração p/ Cães Pedigree Adulto Raças Pequen… 7466      3.39      3.39
    #>  3 Pedigree Ração p/ Cães Pedigree Adulto Raças Pequen… 16425     3.39      3.39
    #>  4 Balance  Alimento p/ Cães Balance Adultos Premium E… 30424     2.99      2.99
    #>  5 Pedigree Alimento p/ Cães Pedigree Nutrição Essenci… 22080   129.99    129.99
    #>  6 Jumbo    Tapete Higiênico Jumbo s/ Fragrância 80x60… 7205     22.99     22.99
    #>  7 Kynus    Ração Kynus p/ Cães Adultos Carne Pacote 1… 22206   109.99    109.99
    #>  8 Doguitos Petisco Dguitos Bifinho de Carne Cães Adul… 7952      7.99      7.99
    #>  9 Pedigree Ração p/ Cães Pedigree Carne 280g           7537     12.99     12.99
    #> 10 Balance  Alimento p/ Cães Balance Adultos Premium E… 30458     2.99      2.99
    #> 11 Pitty    Alimento p/ Cães Pitty Adulto Carne 15Kg    35189    79.9      79.9 
    #> 12 Balance  Alimento p/ Cães Balance Adultos Premium E… 29988     2.99      2.99
    #> 13 Dog Chow Ração Úmida Dog Chow Cães Adultos Minis e … 7443      3.99      3.99
    #> 14 Pedigree Ração p/ Cães Pedigree Filhote Raças Peque… 7378      3.39      3.39
    #> 15 Pedigree Racao p/ Cães Pedigree Adulto Cordeiro 100g 7435      3.39      3.39
    #> 16 Dog Chow Ração Úmida Dog Chow Cães Adultos Minis e … 7255      3.99      3.99
    

    You can also try rvest::read_html_live() that uses headless Chrome browser through chromote package to properly render dynamic content. With up to date Chrome (>= v132) and until current CRAN chromote release (0.3.1) gets updated, make sure to set chromote.headless = "new" option. ( NEWS.md for details).

    To trigger rendering of all elements, we first need to scroll though the page, for this we can use scroll_into_view().

    # for chromote <= 0.3.1 and Chrome >= v132 make sure to switch to new headless mode
    options(chromote.headless = "new")
    
    sess <- read_html_live(url_)
    sess$scroll_into_view(".vtex-render__container-id-shelf-related-search")
    # give it a bit time to render elements
    Sys.sleep(1) 
    
    containers <- html_elements(sess, "#gallery-layout-container  .vtex-product-summary-2-x-container")
    tibble(
      price = html_element(containers, ".promotion-price-by") |> html_text(),
      name  = html_element(containers, "h3") |> html_text()
    )
    #> # A tibble: 16 × 2
    #>    price     name                                                               
    #>    <chr>     <chr>                                                              
    #>  1 R$ 3,39   Ração p/ Cães Pedigree Adulto 7+ Carne Sachê 100g                  
    #>  2 R$ 3,39   Ração p/ Cães Pedigree Adulto Raças Pequenas Carne 100g            
    #>  3 R$ 3,39   Ração p/ Cães Pedigree Adulto Raças Pequenas Frango 100g           
    #>  4 R$ 2,99   Alimento p/ Cães Balance Adultos Premium Especial Cordeiro e Veget…
    #>  5 R$ 129,99 Alimento p/ Cães Pedigree Nutrição Essencial Adultos 9+ Carne ao L…
    #>  6 R$ 22,99  Tapete Higiênico Jumbo s/ Fragrância 80x60cm c/ 7 Unid             
    #>  7 R$ 109,99 Ração Kynus p/ Cães Adultos Carne Pacote 15Kg                      
    #>  8 R$ 7,99   Petisco Dguitos Bifinho de Carne Cães Adultos e Filhotes 65g       
    #>  9 R$ 12,99  Ração p/ Cães Pedigree Carne 280g                                  
    #> 10 R$ 2,99   Alimento p/ Cães Balance Adultos Premium Especial Carne e Vegetais…
    #> 11 R$ 79,90  Alimento p/ Cães Pitty Adulto Carne 15Kg                           
    #> 12 R$ 2,99   Alimento p/ Cães Balance Adultos Premium Especial Frango e Vegetai…
    #> 13 R$ 3,99   Ração Úmida Dog Chow Cães Adultos Minis e Pequenos Frango 100g     
    #> 14 R$ 3,39   Ração p/ Cães Pedigree Filhote Raças Pequenas Frango 100g          
    #> 15 R$ 3,39   Racao p/ Cães Pedigree Adulto Cordeiro 100g                        
    #> 16 R$ 3,99   Ração Úmida Dog Chow Cães Adultos Minis e Pequenos Frango 100g
    

    Created on 2025-01-18 with reprex v2.1.1