Este tutorial adapta pedagógicamente el Día 47. Las secciones 📝 Ejercicio original del curso provienen de los archivos del curso (lecciones 345 a 349). Las soluciones oficiales están en los gists enlazados en cada paso. Los retos 🟢🟡🟠🔴, el proyecto extra y el proyecto integrador son creaciones de esta adaptación. Las correcciones al material original aparecen como 🛠️ Nota técnica.
Nota sobre las verificaciones: el 26 de septiembre de 2026, con Python 3.14, requests 2.34, beautifulsoup4 4.15 y un servidor de correo local (
aiosmtpd), se comprobaron: la extracción del precio del sitio de práctica, los encabezados que envíarequestspor defecto y el envío de la alerta por correo. No se hicieron peticiones a Amazon.
🏫 Para el aula: el paso 4 del curso extrae datos de Amazon en vivo. Las Condiciones de Uso de Amazon prohíben el uso de robots y herramientas de extracción de datos en su sitio, y Amazon suele responder a los bots con un CAPTCHA. En clase, trabaja con el sitio de práctica del curso; el paso 4 puede verse como demostración o análisis. Para tiendas reales, busca primero si ofrecen una API (por ejemplo, Mercado Libre tiene una API pública para desarrolladores).
Tabla de contenidos
- Antes de empezar: que el programa vigile por ti
- Paso 1: extraer el precio del producto
- Paso 2: alerta por correo y datos sensibles en
.env - Paso 3: encabezados HTTP
- Paso 4: el sitio real
- 🚀 Proyecto del día: el rastreador de precios
- 🌟 Proyecto extra: la alerta de libros del plan lector
- 🏆 Proyecto integrador del bloque (Días 45 a 51) — Etapa 3
- 🧩 Comprueba lo aprendido
- 🤖 Programar con apoyo de IA
- 🛠️ Correcciones técnicas al material original
- 📋 Matriz de trazabilidad
- 📦 Evidencias de aprendizaje del Día 47
0. Antes de empezar: que el programa vigile por ti
(Fuente: 345 - Day 47 Goals- what you will make by the end of the day.md)
La profesora quiere comprar una Instant Pot (una olla a presión multifunción), pero no le parece justo pagar 120 dólares. Observó que en las tiendas en línea los precios suben y bajan casi al azar: con camelcamelcamel.com vio el historial de precios del producto y descubrió que el precio más bajo no fue en un día de descuentos como el Black Friday, sino un día cualquiera.
Su solución: un bot de Python que, todos los días a una hora fija, revise el precio y, si baja de un precio objetivo (por ejemplo, 100 dólares), le envíe un correo con el enlace para comprar.
Lo nuevo hoy: convertir texto extraído de una página en número, tomar decisiones con él y combinar scraping con el correo del Día 32. También veremos qué son los encabezados HTTP y por qué los servidores los revisan.
Prerequisitos: Beautiful Soup (Día 45),
smtplib(Día 32), variables de entorno y.env(Días 35 y 38), ejecución programada en PythonAnywhere (Días 32 y 35).
1. Paso 1: extraer el precio del producto
(Fuente: 346-step-1-updated-use-beautifulsoup-to-scrape-the-product-price.md)
🎯 Objetivo de aprendizaje
Extraer un precio de una página y convertirlo en un número de punto flotante (float).
📖 Explicación conceptual
"Los sitios en vivo cambian muchísimo", advierte el curso. Por eso primero se trabaja con un clon estático de la página de Amazon: appbrewery.github.io/instant_pot/.
Del texto al número: lo que obtiene Beautiful Soup siempre es texto (str). Para comparar con un precio objetivo hay que convertirlo en float, quitando antes todo lo que no sea número (el símbolo $, espacios, etc.). La pista del curso es el método split().
Datos verificados del sitio de práctica (26/09/2026):
- El precio completo aparece en un elemento con clase
a-offscreen: texto"$99.99". - También está partido en dos:
a-price-whole("99.", con el punto incluido) ya-price-fraction("99"). - El título del producto está en el elemento con
id="productTitle", rodeado de muchos espacios y saltos de línea.
🔎 Ejemplo explicado
price_text = "$99.99" # lo que devolvió getText()
partes = price_text.split("$") # ['', '99.99']
price = float(partes[1]) # 99.99
print(price, type(price)) # 99.99 <class 'float'>split("$")corta el texto donde aparece$. Como$está al principio, el primer elemento queda vacío.float(...)convierte"99.99"en el número99.99. Si el texto tuviera letras o símbolos, lanzaríaValueError.
Para limpiar el título:
title = " ".join(soup.find(id="productTitle").getText().split())split() sin argumentos corta por cualquier espacio en blanco (espacios, tabulaciones, saltos de línea) y descarta los vacíos; " ".join(...) vuelve a unir con un solo espacio.
⚠️ Errores frecuentes
| Error | Qué ocurre | Solución |
|---|---|---|
float("$99.99") |
ValueError: could not convert string to float |
Quita el $ antes de convertir |
Unir a-price-whole + "." + a-price-fraction |
"99." + "." + "99" = "99..99" → ValueError, porque la parte entera ya trae el punto (verificado) |
Usa el elemento a-offscreen o no agregues otro punto |
Precios con separador de miles ("$1,299.99") |
float falla por la coma |
.replace(",", "") antes de convertir |
En pesos colombianos ("$ 1.299.900") |
El punto es separador de miles, no decimal | Quita los puntos y los espacios; ¡el formato depende del país! |
soup.find(...) devuelve None |
El selector no coincide | Revisa en DevTools; comprueba None antes de usar el resultado |
📝 Ejercicio original del curso: paso 1
Con Beautiful Soup, obtener el precio del artículo del sitio de práctica como float e imprimirlo. (Pista: split().)
🔍 Antes de programar
- ¿Qué tipo de dato devuelve
getText()? ¿Qué tipo necesitas para comparar con 100? - ¿Qué caracteres sobran en el texto del precio?
- ¿En qué elemento está el precio completo? ¿Y si solo encontraras las partes?
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: extrae también el título limpio y la calificación del producto (búscala en DevTools).
- 🟡 Reto 2: escribe una función
texto_a_precio(texto)que funcione con"$99.99","$1,299.99"y"US$ 5". Pruébala con los tres casos. - 🟠 Reto 3: amplía la función para precios en pesos colombianos (
"$ 1.299.900","$89.900"). ¿Cómo sabe la función si el punto es decimal o separador de miles? Decide una regla y documéntala. - 🔴 Reto 4 — Pensamiento computacional: el sitio podría mostrar un precio tachado (el anterior) y el precio actual. Diseña cómo distinguirías cuál es cuál sin depender solo del orden en la página.
2. Paso 2: alerta por correo y datos sensibles en .env
(Fuente: 347-step-2-email-alert-when-the-price-is-below-preset-value.md)
🎯 Objetivo de aprendizaje
Enviar un correo solo cuando se cumple una condición, sin escribir contraseñas en el código.
📖 Explicación conceptual
La condición: si el precio es menor que el objetivo (100 dólares), enviar un correo con el título, el precio actual y el enlace del producto. En el sitio de práctica el precio es fijo ($99.99), así que para probar el envío el objetivo debe ser 100 o más.
Datos sensibles fuera del código (repaso de los Días 35 y 38):
# .env
SMTP_ADDRESS="smtp.gmail.com"
EMAIL_ADDRESS="tu_correo@ejemplo.com"
EMAIL_PASSWORD="contraseña_de_aplicación"import os
from dotenv import load_dotenv
load_dotenv() # lee el archivo .env
smtp_address = os.environ["SMTP_ADDRESS"]Si aparece SMTPServerDisconnected, el curso recomienda:
- Revisar la dirección SMTP del proveedor (Gmail:
smtp.gmail.com; Yahoo:smtp.mail.yahoo.com). - Activar la verificación en dos pasos en la cuenta.
- Crear una contraseña de aplicación y usarla en lugar de la contraseña normal.
- Indicar el puerto:
smtplib.SMTP("smtp.gmail.com", port=587).
🔎 Ejemplo explicado: el correo con tildes
El título del producto del sitio de práctica contiene la palabra "Sauté". Esto importa:
# Estilo del Día 32, con sendmail y un texto:
connection.sendmail(from_addr=..., to_addrs=..., msg=f"Subject:Alerta\n\n{message}")
# -> UnicodeEncodeError: 'ascii' codec can't encode character '\xe9' (verificado)
# Solución 1: convertir el mensaje a bytes UTF-8
connection.sendmail(from_addr=..., to_addrs=..., msg=f"Subject:Alerta\n\n{message}".encode("utf-8"))
# Solución 2 (recomendada): EmailMessage declara la codificación
from email.message import EmailMessage
msg = EmailMessage()
msg["Subject"] = "Alerta de precio"
msg["From"] = email_address
msg["To"] = email_address
msg.set_content(f"{title} ahora cuesta ${price}\n{URL}")
connection.send_message(msg)Con EmailMessage el correo lleva la cabecera charset="utf-8" y las tildes llegan bien (verificado con un servidor local).
🏫 Para el aula: un servidor de correo local
Para probar sin cuentas reales ni contraseñas, usa el servidor de prueba aiosmtpd (el mismo recurso del Día 40):
pip install aiosmtpd
python -m aiosmtpd -n -l localhost:8025Ese servidor no envía nada a Internet: muestra en la terminal cada correo que recibe. En tu programa, conéctate con smtplib.SMTP("localhost", 8025) y sin starttls() ni login().
⚠️ Errores frecuentes
| Error | Causa | Solución |
|---|---|---|
UnicodeEncodeError al enviar |
Texto con tildes enviado con sendmail como str |
.encode("utf-8") o EmailMessage |
SMTPAuthenticationError |
Contraseña normal en lugar de contraseña de aplicación | Crea una contraseña de aplicación (requiere verificación en dos pasos) |
KeyError: 'SMTP_ADDRESS' |
No se cargó el .env o el nombre no coincide |
load_dotenv() antes de leer; revisa mayúsculas |
El .env subido a Internet |
Tus credenciales quedan públicas | .gitignore; si ocurrió, revoca la contraseña de aplicación |
| Un correo cada vez que se ejecuta mientras el precio sigue bajo | Nadie recuerda que ya se avisó | Guarda en un archivo si ya enviaste la alerta (Reto 3) |
📝 Ejercicio original del curso: paso 2
- Si el precio es menor que el objetivo, enviar un correo con título, precio y enlace (Solution 2).
- Crear un
.envconSMTP_ADDRESS,EMAIL_ADDRESSyEMAIL_PASSWORD, y leerlo conosydotenv(Solution 3).
🔍 Antes de programar
- ¿Qué información debe llevar el correo para que sea útil sin abrir el programa?
- ¿Qué datos son sensibles y deben ir en
.env? ¿La URL del producto lo es? - ¿Cómo pruebas el envío si el precio real nunca baja?
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: envía la alerta al servidor local y toma una captura de la terminal donde se ve el correo.
- 🟡 Reto 2: incluye en el correo cuánto se ahorra respecto al precio objetivo y en qué porcentaje.
- 🟠 Reto 3: guarda en
estado.jsonel último precio y si ya se envió la alerta, para avisar una sola vez cada vez que el precio cruce el objetivo. - 🔴 Reto 4 — Pensamiento computacional: en lugar de un precio objetivo fijo, alerta cuando el precio sea el más bajo de los últimos 30 días. ¿Qué datos debes guardar? ¿Qué haces los primeros días, cuando todavía no tienes 30 registros?
3. Paso 3: encabezados HTTP
(Fuente: 348-step-3-add-headers-to-your-request.md)
🎯 Objetivo de aprendizaje
Explicar qué son los encabezados HTTP, verlos en tu navegador y enviarlos con requests.
📖 Explicación conceptual
Cuando tu navegador pide una página, además de la URL envía encabezados (headers): qué navegador usas, qué sistema operativo, qué idiomas prefieres… Con eso el servidor puede responder en tu idioma, tu moneda y tu región.
| Encabezado | Para qué sirve |
|---|---|
User-Agent |
Identifica el navegador y el sistema operativo |
Accept-Language |
Idiomas preferidos, en orden (por ejemplo es-CO,es;q=0.9,en;q=0.8) |
¿Por qué agregarlos? (según el curso)
- El servidor puede darte la página en tu idioma y tu moneda.
- La petición parece "un poco más humana": muchos servidores, como los de Amazon, bloquean lo que creen que viene de bots.
Lo que envía requests si no le dices nada (verificado):
print(requests.get("https://httpbin.org/headers").json()["headers"]["User-Agent"])
# python-requests/2.34.2¡El servidor sabe que eres un programa de Python!
Cómo enviar encabezados:
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"Accept-Language": "es-CO,es;q=0.9",
}
response = requests.get(URL, headers=header)Actividad del curso: visita myhttpheader.com (o httpbin.org/headers) con varios navegadores (Chrome, Firefox, Brave…) y compara lo que envía cada uno. Copia como mínimo el User-Agent y el Accept-Language a tu código (sin Host ni X-Amzn-Trace-Id).
📖 Reflexión ética (adaptación)
Enviar un User-Agent de navegador es una práctica común, pero conviene ser honesto sobre su propósito: sirve para recibir la versión correcta de la página, no para burlar un bloqueo que el sitio puso a propósito. Si un sitio responde con un CAPTCHA, te está diciendo que no quiere bots: la respuesta ética es buscar una API o dejar de hacer peticiones, no intentar "engañarlo". Algunos desarrolladores prefieren un User-Agent que se identifique, por ejemplo "ProyectoEscolar/1.0 (contacto@colegio.edu.co)", para que el dueño del sitio sepa quién hace las peticiones.
⚠️ Errores frecuentes
| Error | Qué ocurre | Solución |
|---|---|---|
requests.get(URL, header) |
El segundo argumento posicional es params, no headers: los encabezados se envían como parámetros de la URL |
requests.get(URL, headers=header) |
Escribir "User Agent" (sin guion) |
El servidor no lo reconoce | "User-Agent" |
Copiar la cabecera Host de otro sitio |
Petición inválida o dirigida a otro servidor | No copies Host |
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: imprime todos los encabezados que envía
requestspor defecto (conhttpbin.org/headers) y luego los que envía con tu diccionario. Compara. - 🟡 Reto 2: pide
httpbin.org/headersconAccept-Languageen español y luego en japonés (ja). ¿Cambia algo en la respuesta de httpbin? ¿Por qué en ese sitio no cambia y en una tienda sí podría cambiar? - 🟠 Reto 3: investiga qué significa
q=0.9enAccept-Languagey escribe el encabezado de alguien que prefiere español de Colombia, luego español en general y por último inglés. - 🔴 Reto 4: escribe una función
pedir_pagina(url)que agregue siempre tus encabezados, espere un tiempo mínimo entre peticiones al mismo dominio y devuelvaNone(con un mensaje) si la respuesta no es 200. Úsala en todos tus scrapers de ahora en adelante.
4. Paso 4: el sitio real
(Fuente: 349-step-4-scrape-the-live-amazon-site.md)
📝 Contenido original del curso
- Elegir un producto de Amazon (o usar el de la profesora) y copiar su URL.
- Inspeccionar el precio y el título: puede que
soup.find()necesite otros selectores que en el sitio de práctica. - Cambiar la URL del sitio de práctica por la del producto real.
- Ejecutar e imprimir
soup.prettify()para verificar que llegó la página del producto y no una página de CAPTCHA.
Si no funciona, el curso sugiere:
- Usar la tienda de tu región (por ejemplo,
amazon.co.uken el Reino Unido). - Probar con pocos o con muchos encabezados.
- Cambiar el analizador a
"lxml"(instalando el paquete). Si aparecebs4.FeatureNotFound: Couldn't find a tree builder with the features you requested, falta instalarlxmlo el nombre del analizador está mal escrito.
📖 Análisis para el aula (adaptación)
Este paso enseña algo valioso: la diferencia entre un entorno de práctica y el mundo real. En el mundo real:
- el HTML cambia sin avisar;
- el sitio puede detectar el bot y responder con un CAPTCHA;
- los términos de uso del sitio pueden prohibir la extracción automática (es el caso de Amazon).
Por eso, antes de apuntar a un sitio real, pregúntate: ¿tiene API?, ¿qué dice su robots.txt?, ¿qué dicen sus términos de uso?, ¿cuántas peticiones haré? (Día 45, sección 4).
🛠️ Nota técnica: el mensaje de error que cita el curso dice
html-parser(con guion). Ese nombre de analizador no existe: el correcto es"html.parser"(con punto). Un error de escritura en el nombre produce exactamente eseFeatureNotFound, aunquelxmlno tenga nada que ver.
Actividad de análisis (en lugar de ejecutar contra Amazon)
- Abre en el navegador la página de un producto de Amazon y mira con DevTools dónde está el precio. ¿Coincide con las clases del sitio de práctica?
- Abre
https://www.amazon.com/robots.txt. ¿Qué rutas prohíbe? - Busca en las Condiciones de Uso de Amazon el apartado sobre robots y extracción de datos. Resume en dos líneas lo que dice.
- Conclusión escrita: ¿harías este paso con un producto real? ¿Qué alternativa propones?
5. 🚀 Proyecto del día: el rastreador de precios
(Integra los pasos 1 a 4 del curso: 346 a 349.)
Problema
Revisar a mano el precio de un producto todos los días es aburrido y fácil de olvidar.
Contexto
Sirve para comprar útiles escolares, un computador para el colegio o el regalo de un familiar al mejor precio.
Objetivo
Un programa que revise el precio del producto del sitio de práctica y envíe una alerta por correo cuando baje del objetivo.
Requisitos funcionales
- Descargar la página con encabezados.
- Extraer título y precio (como
float). - Comparar con un precio objetivo definido como constante.
- Si el precio es menor, enviar un correo con título, precio y enlace.
- Credenciales en
.env.
Restricciones técnicas
- Probar el correo con el servidor local.
- Funciones separadas:
obtener_producto(url),debe_alertar(precio, objetivo),enviar_alerta(titulo, precio, url). EmailMessageo mensaje codificado en UTF-8.
Planificación
- Comprender. 2. Entradas: URL y precio objetivo. 3. Procesos: descargar → extraer → convertir → comparar → avisar. 4. Salidas: correo o mensaje "sin cambios". 5. Dividir en funciones. 6. Diseñar. 7. Implementar. 8. Probar. 9. Corregir. 10. Mejorar.
Construcción paso a paso
- Versión 1: imprime el precio como
float. - Versión 2: imprime "¡Comprar!" o "Esperar" según el objetivo.
- Versión 3: envía la alerta al servidor local.
- Versión 4: mueve los datos sensibles a
.envy agrega encabezados. - Versión final: programa la ejecución diaria (por ejemplo, con las tareas programadas de PythonAnywhere, Día 35) y evita alertas repetidas.
Pruebas
| Caso | Objetivo | Resultado esperado |
|---|---|---|
| Precio menor que el objetivo | 100 | Se envía un correo con "$99.99" |
| Precio mayor que el objetivo | 50 | No se envía nada; mensaje "Esperar" |
| Precio igual al objetivo | 99.99 | Decide y documenta: ¿< o <=? |
| Título con tildes | — | El correo llega sin UnicodeEncodeError |
.env sin una variable |
— | Mensaje claro, no un KeyError misterioso |
🧪 Prueba y depuración
Encuentra los cuatro errores:
TARGET = "100"
response = requests.get(URL, header)
soup = BeautifulSoup(response.content, "html-parser")
price = soup.find(class_="a-offscreen").getText().split("$")[1]
if price < TARGET:
enviar_alerta(price)(Pista: tipos de datos, parámetros de requests, el nombre del analizador y una comparación entre textos que "funciona" pero da resultados absurdos: prueba "99.99" < "100" y "5" < "100" en la consola.)
⭐ Extensión del proyecto
- Vigilar varios productos leídos de un archivo CSV (URL y precio objetivo).
- Guardar el historial de precios en un CSV y graficarlo (Días 72 y 73 del curso, o una gráfica sencilla con pandas).
- Enviar la alerta por Telegram o SMS (Días 35 y 36) en lugar de correo.
6. 🌟 Proyecto extra: la alerta de libros del plan lector
(Proyecto creado para esta adaptación; no existe en el curso original.)
Situación
El club de lectura tiene un presupuesto limitado y quiere comprar libros cuando estén por debajo de cierto precio. Para practicar, usarán la librería ficticia books.toscrape.com (Día 45).
Requisitos
- Un archivo
deseos.csvcon columnasurlyprecio_maximo(al menos 5 libros de la librería de práctica). - Para cada libro: extraer título, precio y disponibilidad (In stock).
- Un solo correo (al servidor local) con todos los libros que están por debajo de su precio máximo y disponibles, ordenados de mayor a menor ahorro.
- Si ninguno cumple, no enviar nada.
- Esperar 1 segundo entre peticiones.
Decisiones que debes tomar (y justificar)
- ¿Un correo por libro o uno con todos? (Piensa en quien lo recibe.)
- ¿Qué haces si una URL del CSV está mal escrita o devuelve 404?
- ¿Cómo representas el precio en libras (£) en el correo?
Casos de prueba
- Un libro con precio máximo muy alto (siempre alerta) y otro muy bajo (nunca).
- Una URL inválida en el CSV: el programa debe continuar con las demás.
7. 🏆 Proyecto integrador del bloque (Días 45 a 51) — Etapa 3
(Proyecto creado para esta adaptación. La situación problema está en el Día 45; la versión final, en el Día 51.)
Etapa 3 (hoy): vigilar cambios y avisar
- Agrega a la página de tu grupo (bloque web) un elemento con un dato que cambia, por ejemplo
<p id="cupos">Cupos disponibles: 12</p>enunete.html. - Escribe
vigilante.py, que lea ese archivo con Beautiful Soup, extraiga el número de cupos y:- guarde el valor y la fecha en
historial_cupos.csv; - envíe un correo al servidor local cuando los cupos bajen de 5 ("¡Quedan pocos cupos!") o lleguen a 0 ("Inscripciones cerradas"), una sola vez por cada cambio de estado.
- guarde el valor y la fecha en
- Prueba editando el HTML a mano (12 → 4 → 0) y ejecutando el programa después de cada cambio.
- Entregable:
vigilante.py, el CSV del historial y la captura de los correos en el servidor local.
8. 🧩 Comprueba lo aprendido
Conceptuales
- ¿Por qué el curso practica primero con un clon estático de Amazon?
- ¿Qué son los encabezados HTTP? Da dos ejemplos y su función.
- ¿Qué es una contraseña de aplicación y por qué se usa?
- ¿Por qué
"99.99" < "100"daFalseaunque 99.99 sea menor que 100? - ¿Qué diferencia hay entre usar un encabezado para recibir la página correcta y usarlo para burlar un bloqueo?
Análisis de código
precios = ["$12.50", "$7.99", "$120.00"]
numeros = [float(p.split("$")[1]) for p in precios]
baratos = [n for n in numeros if n < 20]
print(min(numeros), baratos)¿Qué imprime?
Predicción
print(" ".join("\n Instant Pot \n Duo ".split()))
print("99.".split(".") )Depuración
load_dotenv
email = os.environ["EMAIL"]
with smtplib.SMTP(os.environ["SMTP_ADDRESS"]) as c:
c.login(email, os.environ["PASSWORD"])El programa falla con KeyError. Encuentra dos causas posibles y un tercer problema (pista: ¿dónde está starttls() y en qué puerto se conecta?).
Modificación
Cambia el rastreador para que avise también cuando el precio suba más de un 10 % respecto a la última ejecución.
Creación
Crea un programa que lea quotes.toscrape.com una vez al día y te envíe (al servidor local) la primera cita de la portada solo si es distinta a la del día anterior.
9. 🤖 Programar con apoyo de IA
Ciclo de trabajo: formula el problema → inténtalo tú → consulta a la IA → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.
Actividad 1 — Ética de una respuesta. Pregúntale a una IA: "¿Cómo evito que Amazon detecte mi bot de scraping?". Analiza la respuesta: ¿menciona los términos de uso de Amazon? ¿Sugiere técnicas para burlar protecciones? Escribe una respuesta alternativa que un desarrollador responsable daría, basada en la sección 4.
Actividad 2 — Comprobar datos técnicos. Pide a una IA la dirección SMTP y el puerto de Gmail y de Outlook. Compara con la documentación oficial de cada proveedor (y con la nota técnica de la sección 10). ¿Coinciden?
Actividad 3 — Casos límite. Pide a una IA una función que convierta precios de texto en números. Pruébala con "$1,299.99", "$ 1.299.900" (pesos), "Gratis" y "". ¿Cuáles falla? Mejórala tú.
10. 🛠️ Correcciones técnicas al material original
| Archivo | Afirmación o comportamiento del curso | Observación | Tratamiento |
|---|---|---|---|
349 |
Mensaje de error con html-parser |
El analizador se llama "html.parser"; con guion se produce FeatureNotFound sin importar si lxml está instalado |
Nota técnica de la sección 4 |
349 |
Extraer datos de Amazon en vivo | Las Condiciones de Uso de Amazon prohíben robots y herramientas de extracción; en el aula se usa el sitio de práctica | Aviso inicial; sección 4 |
347 |
Hotmail: smtp.live.com; Outlook: outlook.office365.com |
outlook.office365.com es el servidor para recibir (IMAP/POP). Para enviar, Microsoft indica smtp-mail.outlook.com (cuentas personales) o smtp.office365.com (cuentas de trabajo o estudio). Además, Microsoft ha anunciado el retiro de la autenticación con contraseña para SMTP en cuentas personales: consulta su documentación vigente |
Actividad 2 de IA |
347 |
Mensaje de correo como texto | El título del producto de práctica contiene "Sauté": con sendmail y un str se produce UnicodeEncodeError (verificado) |
Ejemplo de la sección 2 |
346 |
Pista: usar split() |
El elemento a-price-whole trae el punto incluido ("99.", verificado); unir las partes con otro punto produce "99..99" |
Tabla de errores de la sección 1 |
345 |
Ejecutar el bot "todos los días a las 9:00" | El curso no incluye ese paso en las lecciones escritas; se propone con PythonAnywhere (Día 35) | Versión final del proyecto |
11. 📋 Matriz de trazabilidad
| Contenido original | Archivo de origen | Concepto desarrollado | Ejercicio original | Reto adicional | Proyecto relacionado |
|---|---|---|---|---|---|
Instant Pot, variación de precios, camelcamelcamel, precio objetivo, alerta por correo diaria |
345 |
Sección 0 | — | — | Secciones 5 y 6 |
Sitio de práctica instant_pot, precio como float, split() |
346 |
Sección 1 | Paso 1 | Retos 1–4 de la sección 1 | Secciones 5, 6 y 7 |
Alerta si el precio es menor al objetivo, título, precio y enlace, .env, dotenv, SMTPServerDisconnected, direcciones SMTP, verificación en dos pasos, contraseña de aplicación, puerto 587 |
347 |
Sección 2 | Paso 2 (soluciones 2 y 3) | Retos 1–4 de la sección 2 | Secciones 5, 6 y 7 |
Encabezados, myhttpheader.com, User-Agent, Accept-Language, httpbin.org/headers, bloqueo de bots |
348 |
Sección 3 | Paso 3 (solución 4) | Retos 1–4 de la sección 3 | Secciones 5 y 6 |
Amazon en vivo, cambios de HTML, CAPTCHA, prettify, otra región, analizador lxml, FeatureNotFound |
349 |
Sección 4 | Paso 4 (solución 5) | Actividad de análisis | Sección 5 |
| (No existe en el curso original) | — | — | — | Alerta de libros del plan lector | Sección 6 |
| (No existe en el curso original) | — | — | — | Asistente automático, etapa 3 | Sección 7 |
12. 📦 Evidencias de aprendizaje del Día 47
- Función
texto_a_preciocon sus casos de prueba (incluidos precios en pesos). - Captura del servidor local recibiendo la alerta con tildes correctas.
- Tu archivo
.env.ejemplo(sin datos reales) y el.gitignore. - Comparación de encabezados:
requestspor defecto contra tu diccionario. - La actividad de análisis del paso 4 (sitio real,
robots.txty términos de uso). - El rastreador de precios con la tabla de pruebas diligenciada.
- La alerta de libros del plan lector (proyecto extra).
- Etapa 3 del proyecto integrador:
vigilante.pyy su historial. - Cuaderno de errores:
floatcon símbolos,"99..99", comparación de textos,UnicodeEncodeError,headerspasado como posicional,html-parser, alertas repetidas.