Eres analista de datos en una startup de delivery de alimentos saludables que busca fortalecer su estrategia comercial en la categoría de verduras. El equipo necesita entender cómo está estructurada la oferta de plazaVea, uno de los supermercados más importantes del Perú, para identificar:
A partir de técnicas de web scraping en R, deberás recolectar información desde la página de la categoría Verduras y construir tablas que permitan responder preguntas de negocio útiles para la toma de decisiones comerciales.
library(rvest)
library(httr)
library(dplyr)
library(stringr)
library(tidyr)
library(ggplot2)
Paso 1: Leer la página web
url <- "https://www.plazavea.com.pe/frutas-y-verduras/verduras"
pagina_web <- GET(
url,
user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Safari/537.36")
)
contenido <- content(pagina_web, "text", encoding = "UTF-8")
doc <- read_html(contenido)
texto_total <- doc %>% html_text2()
Pregunta 1: ¿Cuántos productos hay en total en la categoría Verduras?
total_productos <- texto_total %>%
str_extract("Produtos encontrados:\\s*\\d+") %>%
str_extract("\\d+") %>%
as.numeric()
print(paste("Total de productos encontrados:", total_productos))
## [1] "Total de productos encontrados: 315"
Este indicador permite dimensionar el tamaño de la categoría y sirve como base para calcular participaciones relativas de subcategorías, marcas y promociones.
Pregunta 2: ¿Cuáles son las principales subcategorías de verduras?
subcategorias_df <- doc %>%
html_elements("a") %>%
html_text2() %>%
str_trim() %>%
.[str_detect(., "\\(\\d+\\)$")] %>%
.[str_detect(., "Zapallo|Cebolla|Brócoli|Apio|Lechuga|Hierbas|Arveja|Tomate|Papa|Zanahoria|Verduras Congeladas|Hongos|Ensaladas|Limón|Choclo|Verduras Orientales")] %>%
unique() %>%
tibble(texto = .) %>%
mutate(
subcategoria = str_remove(texto, "\\s*\\(\\d+\\)$"),
n_productos = as.numeric(str_extract(texto, "\\d+")),
participacion = round(100 * n_productos / total_productos, 2)
) %>%
select(subcategoria, n_productos, participacion) %>%
arrange(desc(n_productos))
subcategorias_df
## # A tibble: 16 × 3
## subcategoria n_productos participacion
## <chr> <dbl> <dbl>
## 1 Lechuga, Espinaca y Hojas Verdes 59 18.7
## 2 Verduras Congeladas 44 14.0
## 3 Hongos, Setas y Germinados 31 9.84
## 4 Ensaladas y Verduras Picadas 31 9.84
## 5 Papa, Camote, Yuca y Otros Tubérculos 25 7.94
## 6 Cebolla, Ajo, Rocoto y Ají 20 6.35
## 7 Hierbas e Infusiones 17 5.4
## 8 Tomate, Pepino y Pimiento 17 5.4
## 9 Apio, Espárrago y Otros Tallos 13 4.13
## 10 Brócoli, Coliflor, Alcachofa y Col 12 3.81
## 11 Choclo 12 3.81
## 12 Zanahoria, Beterraga, Rabanito y Otras Raíces 11 3.49
## 13 Zapallo, Berenjena y Caigua 7 2.22
## 14 Arveja, Vainita, Haba y Otras Legumbres 7 2.22
## 15 Limón 5 1.59
## 16 Verduras Orientales 5 1.59
Esta tabla permite identificar cuáles son las familias de productos con mayor amplitud de surtido dentro de la categoría.
Pregunta 3: ¿Qué subcategorías concentran la mayor oferta?
top_subcategorias <- subcategorias_df %>%
slice_max(order_by = n_productos, n = 5)
top_subcategorias
## # A tibble: 5 × 3
## subcategoria n_productos participacion
## <chr> <dbl> <dbl>
## 1 Lechuga, Espinaca y Hojas Verdes 59 18.7
## 2 Verduras Congeladas 44 14.0
## 3 Hongos, Setas y Germinados 31 9.84
## 4 Ensaladas y Verduras Picadas 31 9.84
## 5 Papa, Camote, Yuca y Otros Tubérculos 25 7.94
ggplot(top_subcategorias, aes(x = reorder(subcategoria, n_productos), y = n_productos)) +
geom_col() +
coord_flip() +
labs(
title = "Top 5 subcategorías con mayor número de productos",
x = "Subcategoría",
y = "Número de productos"
)
Las subcategorías más grandes suelen ser las más relevantes comercialmente, ya sea por rotación, estacionalidad o amplitud del portafolio.
Pregunta 4: ¿Qué marcas dominan la categoría Verduras?
marcas_df <- doc %>%
html_elements("a") %>%
html_text2() %>%
str_trim() %>%
.[str_detect(., "\\(\\d+\\)$")] %>%
.[str_detect(., "AGE|ARCOR|AVIKO|BELL'S|BELL'S FRESCOS|BEM BRASIL|EL HUERTO|EL SABOR|FEST|GENÉRICO|GREEN FOOD|GUVA|HUERCASA|LA CHIGUATENA|Lambweston|MANANTIALES|PACCU|PLAZA VEA|PRACTIFOOD|SAN SEN|SOFIT|TODO SANO|VERDE PURO|VIVA|WILLKA")] %>%
unique() %>%
tibble(texto = .) %>%
mutate(
marca = str_remove(texto, "\\s*\\(\\d+\\)$"),
n_productos = as.numeric(str_extract(texto, "\\d+")),
participacion = round(100 * n_productos / total_productos, 2)
) %>%
select(marca, n_productos, participacion) %>%
arrange(desc(n_productos))
marcas_df
## # A tibble: 25 × 3
## marca n_productos participacion
## <chr> <dbl> <dbl>
## 1 PLAZA VEA 125 39.7
## 2 BELL'S 51 16.2
## 3 BELL'S FRESCOS 33 10.5
## 4 GENÉRICO 22 6.98
## 5 VIVA 17 5.4
## 6 PRACTIFOOD 14 4.44
## 7 VERDE PURO 10 3.17
## 8 MANANTIALES 6 1.9
## 9 AGE 5 1.59
## 10 BEM BRASIL 5 1.59
## # ℹ 15 more rows
Este análisis permite detectar concentración de portafolio por marca y evaluar el peso relativo de la marca propia frente a terceros.
Pregunta 5: ¿Qué porcentaje de productos corresponde a la marca propia plazaVea?
marca_propia_df <- marcas_df %>%
filter(marca == "PLAZA VEA") %>%
mutate(
porcentaje_marca_propia = round(100 * n_productos / total_productos, 2)
)
marca_propia_df
## # A tibble: 1 × 4
## marca n_productos participacion porcentaje_marca_propia
## <chr> <dbl> <dbl> <dbl>
## 1 PLAZA VEA 125 39.7 39.7
Una mayor presencia de marca propia puede reflejar estrategia de margen, control de surtido y posicionamiento de valor.
Pregunta 6: ¿Qué marcas tienen mayor presencia dentro de la categoría?
top_marcas <- marcas_df %>%
slice_max(order_by = n_productos, n = 10)
top_marcas
## # A tibble: 11 × 3
## marca n_productos participacion
## <chr> <dbl> <dbl>
## 1 PLAZA VEA 125 39.7
## 2 BELL'S 51 16.2
## 3 BELL'S FRESCOS 33 10.5
## 4 GENÉRICO 22 6.98
## 5 VIVA 17 5.4
## 6 PRACTIFOOD 14 4.44
## 7 VERDE PURO 10 3.17
## 8 MANANTIALES 6 1.9
## 9 AGE 5 1.59
## 10 BEM BRASIL 5 1.59
## 11 PACCU 5 1.59
Pregunta 7: ¿Qué tipos de descuento aparecen en la categoría?
descuentos_df <- doc %>%
html_elements("a") %>%
html_text2() %>%
str_trim() %>%
.[str_detect(., "Tarjeta Oh!|sin descuentos")] %>%
unique() %>%
tibble(texto = .) %>%
mutate(
tipo_descuento = str_remove(texto, "\\s*\\(\\d+\\)$"),
n_productos = as.numeric(str_extract(texto, "\\d+")),
participacion = round(100 * n_productos / total_productos, 2)
) %>%
select(tipo_descuento, n_productos, participacion) %>%
arrange(desc(n_productos))
descuentos_df
## # A tibble: 2 × 3
## tipo_descuento n_productos participacion
## <chr> <dbl> <dbl>
## 1 sin descuentos 287 91.1
## 2 con Tarjeta Oh! 5 1.59
Pregunta 8: ¿Qué proporción del surtido está sin descuentos?
sin_descuento_df <- descuentos_df %>%
filter(str_detect(tipo_descuento, regex("sin descuentos", ignore_case = TRUE)))
sin_descuento_df
## # A tibble: 1 × 3
## tipo_descuento n_productos participacion
## <chr> <dbl> <dbl>
## 1 sin descuentos 287 91.1
Una alta proporción sin descuentos puede indicar estabilidad de precios, menor presión promocional o una categoría menos agresiva comercialmente.
Pregunta 9: ¿Cómo se distribuye la oferta entre plazaVea y Marcas Aliadas?
vendedores_df <- doc %>%
html_elements("a") %>%
html_text2() %>%
str_trim() %>%
.[str_detect(., "plazaVea \\(|Marcas Aliadas \\(")] %>%
unique() %>%
tibble(texto = .) %>%
mutate(
vendedor = str_remove(texto, "\\s*\\(\\d+\\)$"),
n_productos = as.numeric(str_extract(texto, "\\d+")),
participacion = round(100 * n_productos / total_productos, 2)
) %>%
select(vendedor, n_productos, participacion) %>%
arrange(desc(n_productos))
vendedores_df
## # A tibble: 2 × 3
## vendedor n_productos participacion
## <chr> <dbl> <dbl>
## 1 plazaVea 292 92.7
## 2 Marcas Aliadas 23 7.3
Pregunta 10: Construya una base consolidada de estructura de la categoría
estructura_verduras_df <- bind_rows(
subcategorias_df %>%
mutate(tipo = "subcategoria", nombre = subcategoria) %>%
select(tipo, nombre, n_productos, participacion),
marcas_df %>%
mutate(tipo = "marca", nombre = marca) %>%
select(tipo, nombre, n_productos, participacion),
descuentos_df %>%
mutate(tipo = "descuento", nombre = tipo_descuento) %>%
select(tipo, nombre, n_productos, participacion),
vendedores_df %>%
mutate(tipo = "vendedor", nombre = vendedor) %>%
select(tipo, nombre, n_productos, participacion)
)
estructura_verduras_df
## # A tibble: 45 × 4
## tipo nombre n_productos participacion
## <chr> <chr> <dbl> <dbl>
## 1 subcategoria Lechuga, Espinaca y Hojas Verdes 59 18.7
## 2 subcategoria Verduras Congeladas 44 14.0
## 3 subcategoria Hongos, Setas y Germinados 31 9.84
## 4 subcategoria Ensaladas y Verduras Picadas 31 9.84
## 5 subcategoria Papa, Camote, Yuca y Otros Tubérculos 25 7.94
## 6 subcategoria Cebolla, Ajo, Rocoto y Ají 20 6.35
## 7 subcategoria Hierbas e Infusiones 17 5.4
## 8 subcategoria Tomate, Pepino y Pimiento 17 5.4
## 9 subcategoria Apio, Espárrago y Otros Tallos 13 4.13
## 10 subcategoria Brócoli, Coliflor, Alcachofa y Col 12 3.81
## # ℹ 35 more rows