Aquí dejo mi solución de scraping incluyendo la parte de extraer las estrellas del libro, su disponibilidad en stock y el guardado de los datos en un archivo de csv.

Hacer web scraping de un catálogo de libros con Python y BeautifulSoup te permite obtener nombre, precio e imagen de cada producto en pocos minutos. Si estás aprendiendo a automatizar la extracción de datos de páginas web, este recorrido te muestra cómo identificar contenedores HTML, recorrerlos con un loop y guardar los resultados en un archivo CSV listo para analizar.
¿Cómo identifico el contenedor de cada producto en el HTML?
Antes de escribir código, abre las herramientas de desarrollo del navegador con clic derecho y "Inspeccionar". En el sitio de práctica vas a notar que cada libro vive dentro de una etiqueta article con la clase product-pod. Ese contenedor agrupa la imagen, el título en h3, las estrellas y el precio.
Identificar bien ese nodo padre es clave, porque sobre él vas a iterar para extraer las propiedades de cada libro sin mezclar información entre productos.
¿Qué es un selector CSS en BeautifulSoup? Es una cadena que describe qué etiqueta y clase buscar en el HTML. Por ejemplo,
article.product-podselecciona todos losarticlecon la claseproduct-pod.
¿Cómo obtengo el listado de productos con select?
Después de hacer la petición GET y parsear el HTML con soup, defines la variable que contendrá todos los artículos:
python products = soup.select("article.product-pod")
El método select te devuelve una lista de elementos. Si imprimes products, verás cada bloque HTML completo, pero en este punto solo te interesan tres campos por libro: nombre, precio e imagen.
¿Cómo extraigo nombre, precio e imagen de cada producto?
La lógica es recorrer el listado con un for y, dentro del loop, usar find sobre cada product (no sobre soup) para acotar la búsqueda al contenedor del libro actual.
h3 y, más específicamente, en el atributo title de la etiqueta <a> interna. Por eso accedes con product.find("h3").find("a")["title"].<p> con la clase price color. Aplicas .get_text() al final para limpiar la etiqueta y quedarte con el valor.div con clase contenedora. Como el atributo correcto está en la etiqueta img, usas product.find("div", class_="container").find("img")["src"].Un detalle importante: la URL de la imagen es relativa. No incluye www.books.toscrape.com, así que debes concatenar la raíz del sitio con la ruta extraída para obtener la URL completa que sí abre la imagen en el navegador.
¿Por qué uso guion bajo en class_ dentro de find? Porque
classes una palabra reservada en Python. BeautifulSoup adoptaclass_para que puedas filtrar por clase CSS sin romper la sintaxis del lenguaje.
¿Por qué fallaba el atributo href en la imagen?
Un error común es intentar leer href desde una etiqueta <a> cuando lo que necesitas es el src de una etiqueta <img>. Si te aparece un error de atributo inexistente, revisa qué etiqueta estás consultando. En este caso, cambiar a por img y href por src resuelve el problema y te entrega la URL del recurso.
Esta diferencia entre etiquetas y atributos es lo que distingue el scraping funcional del que rompe en producción.
¿Cómo guardo los datos extraídos en un CSV?
Una vez que tienes la lista de diccionarios con los 20 productos, los exportas a un archivo dentro de una carpeta resultados llamado productos.csv. Los parámetros que importan al abrir el archivo son:
"w" para escritura (write).newline="" para que no se inserten saltos de línea adicionales entre filas.encoding configurado para soportar caracteres especiales sin romper el archivo.Las columnas que defines son nombre, precio e imagen_url, y le pasas el listado de productos generado en el loop. Al ejecutar, la consola confirma "extracción completa, 20 productos guardados" y puedes abrir el CSV directamente desde Visual Studio Code para validar el resultado.
El flujo final combina tres habilidades centrales: inspección del DOM, navegación con find y select y persistencia en CSV. Con esa base puedes adaptar el script a cualquier catálogo con estructura similar.
¿Qué reto puedes resolver para practicar?
El ejercicio propuesto es ampliar la extracción para incluir dos campos adicionales por libro:
Déjame tu solución en los comentarios y comparte qué selector usaste para capturar las estrellas, que suelen estar codificadas como clases CSS en lugar de texto plano.
Hacer web scraping de un catálogo de libros con Python y BeautifulSoup te permite obtener nombre, precio e imagen de cada producto en pocos minutos. Si estás aprendiendo a automatizar la extracción de datos de páginas web, este recorrido te muestra cómo identificar contenedores HTML, recorrerlos con un loop y guardar los resultados en un archivo CSV listo para analizar.
Antes de escribir código, abre las herramientas de desarrollo del navegador con clic derecho y "Inspeccionar". En el sitio de práctica vas a notar que cada libro vive dentro de una etiqueta article con la clase product-pod. Ese contenedor agrupa la imagen, el título en h3, las estrellas y el precio.
Identificar bien ese nodo padre es clave, porque sobre él vas a iterar para extraer las propiedades de cada libro sin mezclar información entre productos.
¿Qué es un selector CSS en BeautifulSoup? Es una cadena que describe qué etiqueta y clase buscar en el HTML. Por ejemplo,
article.product-podselecciona todos losarticlecon la claseproduct-pod.
Después de hacer la petición GET y parsear el HTML con soup, defines la variable que contendrá todos los artículos:
python products = soup.select("article.product-pod")
El método select te devuelve una lista de elementos. Si imprimes products, verás cada bloque HTML completo, pero en este punto solo te interesan tres campos por libro: nombre, precio e imagen.
La lógica es recorrer el listado con un for y, dentro del loop, usar find sobre cada product (no sobre soup) para acotar la búsqueda al contenedor del libro actual.
h3 y, más específicamente, en el atributo title de la etiqueta <a> interna. Por eso accedes con product.find("h3").find("a")["title"].<p> con la clase price color. Aplicas .get_text() al final para limpiar la etiqueta y quedarte con el valor.div con clase contenedora. Como el atributo correcto está en la etiqueta img, usas product.find("div", class_="container").find("img")["src"].Un detalle importante: la URL de la imagen es relativa. No incluye www.books.toscrape.com, así que debes concatenar la raíz del sitio con la ruta extraída para obtener la URL completa que sí abre la imagen en el navegador.
¿Por qué uso guion bajo en class_ dentro de find? Porque
classes una palabra reservada en Python. BeautifulSoup adoptaclass_para que puedas filtrar por clase CSS sin romper la sintaxis del lenguaje.
Un error común es intentar leer href desde una etiqueta <a> cuando lo que necesitas es el src de una etiqueta <img>. Si te aparece un error de atributo inexistente, revisa qué etiqueta estás consultando. En este caso, cambiar a por img y href por src resuelve el problema y te entrega la URL del recurso.
Esta diferencia entre etiquetas y atributos es lo que distingue el scraping funcional del que rompe en producción.
Una vez que tienes la lista de diccionarios con los 20 productos, los exportas a un archivo dentro de una carpeta resultados llamado productos.csv. Los parámetros que importan al abrir el archivo son:
"w" para escritura (write).newline="" para que no se inserten saltos de línea adicionales entre filas.encoding configurado para soportar caracteres especiales sin romper el archivo.Las columnas que defines son nombre, precio e imagen_url, y le pasas el listado de productos generado en el loop. Al ejecutar, la consola confirma "extracción completa, 20 productos guardados" y puedes abrir el CSV directamente desde Visual Studio Code para validar el resultado.
El flujo final combina tres habilidades centrales: inspección del DOM, navegación con find y select y persistencia en CSV. Con esa base puedes adaptar el script a cualquier catálogo con estructura similar.
El ejercicio propuesto es ampliar la extracción para incluir dos campos adicionales por libro:
Déjame tu solución en los comentarios y comparte qué selector usaste para capturar las estrellas, que suelen estar codificadas como clases CSS en lugar de texto plano.
Aquí dejo mi solución de scraping incluyendo la parte de extraer las estrellas del libro, su disponibilidad en stock y el guardado de los datos en un archivo de csv.

# List to store the information
product_list = []
for product in products:
# Book name
nombre = product.find("h3").find("a")["title"]
# Price
precio = product.find("p", class_="price_color").get_text()
# Image
imagen = product.find("div", class_="image_container").find("img")["src"]
imagen_url = "http://books.toscrape.com/" + imagen
# Estrellas
estrellas = product.find("p", class_="star-rating")["class"][1]
# Stock
stock = product.find("p", class_="instock availability").get_text(strip=True)
product_list.append({
"name": nombre,
"price": precio,
"image_url": imagen_url,
"stars": estrellas,
"stock": stock
})
# Display first 3 products as preview
print("First 5 products:")
for i, product in enumerate(product_list[:3], 1):
print(f"\n{i}. {product['name']}")
print(f" Price: {product['price']}")
print(f" Image: {product['image_url']}")
print(f" Stars: {product['stars']}")
print(f" Stock: {product['stock']}")
En el contexto de Beautiful Soup, el uso de class_="product_pod" es una forma de referirse a los atributos de un elemento HTML. Sin embargo, al utilizar el método select(), se emplea la notación de CSS, donde el punto . indica una clase. Por eso, significa "selecciona todos los elementos con la clase ". La primera forma es más directa al buscar atributos, mientras que la segunda es más flexible y potente al utilizar selectores CSS.
select(".product_pod")product_podque curso tan bueno que docente tan bueno
Se salto un pedazo al momento de guardar en el excel.. como es la creacion del pandas creo .. para luego ser guardado en el excel
Al usar .text en BeautifulSoup, este obtiene todo el contenido dentro de la etiqueta, incluidos los espacios, saltos de línea y otros caracteres de formato.
Para eliminar esos espacios y saltos de línea innecesarios, puedes utilizar .strip(), que elimina los espacios en blanco y saltos de línea al principio y al final del texto extraído.
product_list = []
for product in products:
#nombre del libro
name = product.find("h3").find("a")["title"]
print(name)
#precio
precio = product.find("p", class_="price_color").get_text()
print(precio)
#imagen
imagen = product.find("div", class_="image_container").find("img")["src"]
imagen_url = url + imagen
print(imagen_url)
#estrellas
estrellas = product.find("p", class_="star-rating").get("class")[1]
print(estrellas)
#stock
stock = product.find("p", class_="instock availability").get_text(strip = True)
print(stock)
pass
product_list.append({"Nombre": name, "Precio": precio, "Imagen_url": imagen_url, "Estrellas": estrellas, "Stock": stock})
Por aquí mi solución al scraping incluyendo el rating y disponibilidad del artículo:
# List of products to store the extracted information
homework_products_list = []
# Loop through the products and extract the title, price, and picture URL
for product in products:
# Title is found in the "h3" tag, and the actual title text is in the "title" attribute of the "a" tag inside the "h3" tag
title = product.find("h3").find("a")["title"]
# Price is found in the "p" tag with the class "price_color", and we use the get_text() method to extract the text content, stripping any extra whitespace
price = product.find("p", class_="price_color").get_text(strip=True)
# Picture URL is found in the "div" tag with the class "image_container", and the actual URL is in the "src" attribute of the "img" tag inside that "div" tag
# Picture URLs on the website are relative, so we need to concatenate the base URL with the relative URL to get the full URL of the picture
picture = product.find("div", class_="image_container").find("img")["src"]
picture_url = url + picture
# Calification is found in the "p" tag with the class "star-rating", and the actual calification is in the second class of that "p" tag
# the first class is "star-rating" and the second class is the calification (e.g., "One", "Two", "Three", "Four", "Five")
calification = product.find("p", class_="star-rating")["class"][1]
# Stock is found in the "p" tag with the class "instock availability", and we use the get_text() method to extract the text content
# stripping any extra whitespace
stock = product.find("p", class_="instock availability").get_text(strip=True)
# Append the extracted information as a dictionary to the products_list
homework_products_list.append({
"title": title,
"price": price,
"picture_url": picture_url,
"calification": calification,
"stock": stock
})
homework_path_csv = "results/homework_book_store_products.csv"
with open(homework_path_csv, "w", newline="", encoding="utf-8") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=["title", "price", "picture_url", "calification", "stock"])
writer.writeheader() # Write the header row to the CSV file
writer.writerows(homework_products_list) # Write the rows of product information to the CSV file
print(f"Data has been successfully written to {homework_path_csv}\nTotal products extracted: {len(homework_products_list)}")
Aqui dejo mi código con el agregado del rating de estrellas y el stock
from bs4 import BeautifulSoup
import requests
import csv
url = "https://books.toscrape.com"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
products = soup.select("article.product_pod")
product_list = []
for product in products:
book_title = product.find("h3").find("a")["title"]
#print(book_title)
price = product.find("p", class_="price_color").get_text()
#print(price)
image = product.find("div", class_="image_container").find("img")["src"]
image_url = "https://books.toscrape.com/" + image
#print(image_url)
rating = product.find("p", class_="star-rating")["class"][1]
#print(rating)
stock = product.find("p", class_="instock").get_text(strip=True)
#print(stock)
product_list.append({"name":book_title, "price":price, "image_url":image_url, "rating":rating, "stock":stock})
pass
path_csv = "results/products.csv"
with open(path_csv, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["name","price","rating","stock","image_url"])
writer.writeheader()
writer.writerows(product_list)
print(f"Complete Extraction: {len(product_list)} products saved in .csv")
