Cómo extraer precios con Python: una guía paso a paso (2026)
Todas las entradas

Cómo extraer precios con Python: una guía paso a paso (2026)

Ryan Turner
Ryan Turner · Head of Growth

La extracción de precios con Python consiste en escribir un script que recupere la página de un producto, localice el precio en el código HTML o en el resultado final, y lo guarde como datos estructurados que pueda comparar o seguir a lo largo del tiempo. La versión más básica consiste en una solicitud HTTP y un analizador de HTML. Las partes más complejas son las defensas contra los bots y los precios renderizados mediante JavaScript, y es precisamente en ello en lo que se centra la mayor parte de esta guía.

Esta es la sección técnica de nuestra guía más amplia sobre monitoreo de precios de la competencia. Si desea obtener una visión general de la estrategia y las herramientas, empiece por ahí. Si desea código ejecutable, siga leyendo.

Puntos clave

  • Un «scraper» de precios básico es un cliente HTTP (solicitudes o httpx) además de un analizador sintáctico (selectolax (o BeautifulSoup) que se centra en un selector CSS.
  • La parte delicada no es el análisis sintáctico, sino el acceso: los límites de frecuencia, el filtrado por agente de usuario, el geo-cloaking y los bloqueos directos de direcciones IP.
  • El uso de proxies residenciales rotativos es importante para las páginas de precios, ya que el precio que se muestra depende del país y de la reputación de la dirección IP desde la que se realiza la solicitud.
  • Los precios generados mediante JavaScript requieren un navegador sin interfaz gráfica, una llamada a la API mediante ingeniería inversa o un servicio de conversión a Markdown.
  • Almacene cada observación como una fila con una marca de tiempo y la divisa, de modo que pueda realizar un seguimiento de los cambios, y no solo de instantáneas. Respete las condiciones de uso y el archivo robots.txt de cada sitio web.

¿Qué es el «price scraping»?

El «price scraping» consiste en la extracción automatizada de los precios de los productos (y, por lo general, de campos relacionados como el título, la moneda, la disponibilidad y el SKU) de páginas web. Los minoristas lo hacen para supervisar a la competencia. Las marcas lo hacen para garantizar el cumplimiento de los precios mínimos anunciados. Los agregadores lo hacen para alimentar Comparación de precios de web scraping sitios web.

Los precios son un objetivo de gran valor que se defiende activamente. El Informe Imperva Bad Bot de 2025 (una empresa de Thales) reveló que el tráfico automatizado superó a la actividad humana por primera vez en una década, representando el 51 % de todo el tráfico web en 2024, mientras que los bots maliciosos alcanzaron el 37 %. El comercio electrónico se encuentra entre los sectores más atacados, por lo que las páginas de precios cuentan con las mismas defensas que bloquean a los bots dedicados al «credential stuffing» y al acaparamiento de existencias. Su rastreador debe parecer un visitante normal.

Paso 1: Un rastreador de precios en Python muy sencillo

Empecemos por lo más sencillo que pueda funcionar: una sola solicitud GET y un único analizador. Utilizamos httpx (una moderna solicitudes(cliente compatible) y selectolax (un analizador HTML rápido basado en lexbor).


pip install httpx selectolax


import httpx
from selectolax.parser import HTMLParser

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/126.0.0.0 Safari/537.36"
    ),
    "Accept-Language": "en-US,en;q=0.9",
}

def scrape_price(url: str, price_selector: str) -> str | None:
    resp = httpx.get(url, headers=HEADERS, timeout=20.0, follow_redirects=True)
    resp.raise_for_status()
    tree = HTMLParser(resp.text)
    node = tree.css_first(price_selector)
    return node.text(strip=True) if node else None

if __name__ == "__main__":
    price = scrape_price("https://example.com/product/123", "span.price")
    print(price)

pip install httpx selectolax

import httpx
from selectolax.parser import HTMLParser

HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
}

def scrape_price(url: str, price_selector: str) -> str | None:
resp = httpx.get(url, headers=HEADERS, timeout=20.0, follow_redirects=True)
resp.raise_for_status()
tree = HTMLParser(resp.text)
node = tree.css_first(price_selector)
return node.text(strip=True) if node else None

if __name__ == "__main__":
price = scrape_price("https://example.com/product/123", "span.price")
print(price)

La única parte específica del lugar es selector_de_precio. Abra la página del producto en su navegador, examine el elemento «precio» y copie un selector estable (una clase o datos- atributo, no un div > div > div (una cadena que se rompe en la siguiente implementación).

Los resultados se muestran como cadenas de caracteres desordenadas, como «1 299,00 $» o «1.299,00 €». Normalice los datos antes de almacenarlos:

import re
from decimal import Decimal

def parse_amount(raw: str) -> Decimal | None:
if not raw:
return None
# Eliminar todo excepto los dígitos y los separadores
cleaned = re.sub(r"[^\d.,]", "", raw)
# Heurística: si la coma es el separador decimal (p. ej., «1.299,00»)
si cleaned.count(",") == 1 y cleaned.rfind(",") > cleaned.rfind("."):
cleaned = cleaned.replace(".", "").replace(",", ".")
else:
cleaned = cleaned.replace(",", "")
try:
return Decimal(cleaned)
except Exception:
return None

Mantenga siempre el símbolo de la moneda original por separado. No dé por sentado que se trata del USD.

Paso 2: Gestionar las medidas de protección contra los bots

El ejemplo anterior funciona en una página de prueba, pero falla en la mayoría de las tiendas online reales. A continuación le indicamos los problemas con los que se encontrará y cómo resolver cada uno de ellos.

Límites de frecuencia y reintentos

Bombardear un sitio web es la forma más rápida de que le bloqueen el acceso y lo más descortés que puede hacer. Añada retrasos, fluctuaciones y retroceso exponencial a las respuestas que le indican que «reduzca la velocidad» (429) o «temporalmente no disponible» (503).

import random
import time
import httpx

def get_with_backoff(client: httpx.Client, url: str, max_tries: int = 4):
for attempt in range(max_tries):
resp = client.get(url, timeout=20.0)
si resp.status_code no está en (429, 503):
devuelva resp
espera = (2 ** intento) + random.uniform(0, 1.0)
time.sleep(wait)
resp.raise_for_status()
return resp

Agentes de usuario y encabezados

Una solicitud sin encabezados delata claramente que se trata de un «script». Envíe un User-Agent realista y actual de un navegador, así como los encabezados que envía un navegador real (Aceptar, Accept-Language, Accept-Encoding). Alternar entre un pequeño conjunto de cadenas de UA reales y recientes ayuda, pero una UA creíble, combinada con una dirección IP marcada, sigue siendo bloqueada.

Bloques de direcciones IP, enmascaramiento geográfico y la importancia de los proxies residenciales

Esta es la parte que la mayoría de los tutoriales omiten. Dos solicitudes dirigidas a la misma URL de un producto pueden mostrar precios distintos, divisas diferentes o una página de bloqueo, dependiendo exclusivamente de la dirección IP desde la que se realice la solicitud.

  • Direcciones IP del centro de datos (los rangos que utilizan la mayoría de los servidores en la nube) son fáciles de identificar y, con frecuencia, están sujetos a limitaciones de velocidad o directamente bloqueados en las páginas de precios.
  • Ocultación geográfica Esto significa que un sitio web muestra el precio en dólares a un visitante de EE. UU. y el precio en euros a un visitante de Alemania. Si se recopilan datos desde una única región de centro de datos, solo se ven los precios de un mercado y, en ocasiones, aparece el mensaje «No disponible en su región».

Los proxies residenciales rotativos redirigen las solicitudes a través de conexiones reales de usuarios, por lo que presentan la reputación de IP de un visitante habitual y le permiten elegir el país (y, en ocasiones, la ciudad) desde el que parece proceder la solicitud. En el caso de la recopilación de precios, esto significa que podrá ver el precio real localizado para cada mercado y evitar los bloqueos de IP de centros de datos que impiden el acceso a estas páginas. Massive gestiona una red residencial en más de 195 países (HTTP/HTTPS/SOCKS5) con segmentación geográfica por país y ciudad, y sesiones rotativas o fijas.

Para configurar un proxy en el cliente basta con cambiar una sola línea:

import os
import httpx

# Proxy residencial de Massive. Las credenciales se introducen en la URL del proxy en el formato usuario:contraseña.
PROXY = (
f"https://{os.environ['MASSIVE_PROXY_USERNAME']}:"
f"{os.environ['MASSIVE_API_KEY']}@network.joinmassive.com:65535"
)

with httpx.Client(proxy=PROXY, headers=HEADERS, timeout=20.0) as client:
resp = client.get("https://example.com/product/123")
print(resp.status_code)

Utilice un giratorio sesión cuando desee una dirección IP nueva para cada solicitud (ideal para distribuir la carga) y una destacado sesión cuando un flujo necesita la misma dirección IP en varias solicitudes (por ejemplo, establecer una cookie de región y, a continuación, cargar el precio). Para obtener una guía paso a paso específica para minoristas, consulte Extraer los precios de Amazon sin que le bloqueen el acceso.

Paso 3: Gestionar los precios generados mediante JavaScript

Muchas páginas web envían una estructura HTML prácticamente vacía y calculan el precio en el navegador mediante JavaScript. Si resp.text Si no contiene el precio, el analizador del paso 1 devuelve Ninguno por muy bueno que sea su selector. Tiene tres opciones.

Opción A: Busque la API subyacente. Abra DevTools, vaya a la pestaña «Red», filtre por «XHR/Fetch» y actualice la página. El precio casi siempre se recibe en una respuesta JSON. Acceder directamente a ese punto final es más rápido y más estable que la visualización, siempre que la propia API no esté restringida.

Opción B: Utilizar un navegador sin interfaz gráfica. Playwright representa la página en Chromium real, por lo que el precio ya figura en el DOM en el momento en que lo lee.

from playwright.sync_api import sync_playwright

def scrape_rendered_price(url: str, selector: str) -> str | None:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
página = navegador.new_page()
página.goto(url, wait_until="networkidle")
nodo = página.query_selector(selector)
valor = nodo.inner_text() si nodo; en caso contrario, None
navegador.close()
return valor

Los navegadores sin interfaz gráfica consumen muchos recursos: utilizan mucha más CPU y memoria que una solicitud HTTP y su escalabilidad es más lenta.

Opción C: Convertir a Markdown (la forma más sencilla). Esta es la vía más sencilla. Una Web Render API ejecuta el renderizado sin interfaz gráfica en la infraestructura de un tercero: navegadores reales que ejecutan el JavaScript de la página, y el resultado final se convierte a Markdown limpio y se devuelve a su propio código. La Web Render API de Massive cuenta con un punto final de navegación que hace exactamente esto. Se evita tanto la sobrecarga del navegador sin interfaz gráfica como la mayor parte del trabajo de análisis sintáctico del HTML, ya que se busca la línea del precio en texto legible en lugar de recorrer un DOM inestable, y el Markdown se introduce directamente en un cliente de IA o en una solicitud de LLM si desea extraer el precio de esa manera.

import os
import re
import httpx

# Web Render API de Massive, punto final de navegación: renderiza la página en
# la red de Massive y devuelve el código Markdown limpio.
def render_markdown(url: str, country: str = "US") -> str:
resp = httpx.get(
"https://render.joinmassive.com/browser",
params={"url": url, "format": "markdown", "country": country},
headers={"Authorization": f"Bearer {os.environ['MASSIVE_API_TOKEN']}"},
timeout=60.0, # la renderización es más pesada que una simple solicitud GET, así que hay que darle margen
)
resp.raise_for_status()
return resp.text

# El Markdown es mucho más fácil de analizar que el HTML sin procesar. Pase el país para ver el
# precio tal y como lo ve un comprador local (precio en USD, en EUR, etc.).
markdown = render_markdown("https://example.com/product/123")
match = re.search(r"\$[\d,]+\.\d{2}", markdown)
price = match.group(0) if match else None

Paso 4: Estructurar y almacenar los datos

Un precio solo resulta útil como serie temporal. Almacene cada observación en una fila independiente con una marca de tiempo, de modo que pueda detectar caídas, realizar un seguimiento de la competencia y elaborar un sistema de monitoreo de precios encima.

import sqlite3
from datetime import datetime, timezone
from decimal import Decimal

def init_db(path: str = "prices.db") -> sqlite3.Connection:
conn = sqlite3.connect(path)
conn.execute(
"""
CREATE TABLE IF NOT EXISTS observations (
id INTEGER PRIMARY KEY,
sku TEXT NOT NULL,
url TEXT NOT NULL,
amount TEXT NOT NULL, -- almacene Decimal como texto, nunca como float
currency TEXT NOT NULL,
country TEXT, -- de qué mercado procede este precio
scraped_at TEXT NOT NULL
)
"""
)
return conn

def record(conn, sku: str, url: str, amount: Decimal, currency: str, country: str):
conn.execute(
"INSERT INTO observations (sku, url, amount, currency, country, scraped_at) "
"VALUES (?, ?, ?, ?, ?, ?)",
(sku, url, str(amount), currency, country,
datetime.now(timezone.utc).isoformat()),
)
conn.commit()

Dos reglas que le serán de gran ayuda más adelante: guarde el importe como texto o como un recuento entero de unidades menores (nunca como un número flotante binario, ya que se pierden céntimos), y registre siempre el moneda y país. Un «$50» y un «50 EUR» en la misma columna sin indicación de moneda constituyen un error de datos oculto. Tanto para un archivo CSV como para un canal de integración de datos, el esquema es el mismo; la fila con marca de tiempo y etiqueta de mercado es la unidad que importa. Si prefiere adquirir en lugar de desarrollar, compare herramientas de seguimiento de los precios de la competencia.

Nota sobre la legalidad y las condiciones de uso

Esto no constituye asesoramiento jurídico, pero conviene conocer la situación práctica. La recopilación de datos de acceso público ha superado en repetidas ocasiones los recursos interpuestos en Estados Unidos en virtud de la Ley de Fraude y Abuso Informático: en el prolongado hiQ Labs contra LinkedIn En el marco de un litigio, el Noveno Circuito reafirmó en 2022 que el acceso a datos de sitios web disponibles públicamente probablemente no constituya un acceso «sin autorización» a efectos de la CFAA. Dicha sentencia se refería específicamente a la CFAA, no suponía una autorización general. Aún pueden aplicarse otras teorías (incumplimiento de las condiciones de uso de un sitio web, derechos de autor, usurpación de bienes muebles y la legislación en materia de privacidad).

Medidas de seguridad prácticas: lea y respete las condiciones de uso de cada sitio web y robots.txt, recopile únicamente datos públicos sobre precios (nunca nada que esté protegido por un inicio de sesión y que se haya comprometido a no recopilar), aplique límites de frecuencia para no afectar al rendimiento del sitio web y evite los datos personales. En caso de duda, consulte con el departamento jurídico.

Constrúyalo en una red que refleje el precio real

El código es la parte fácil. Lo difícil es disponer de acceso constante a precios correctos y adaptados a cada mercado, y todo depende de dónde parezca que se originan sus solicitudes. La red residencial de Massive (más de 195 países, segmentación geográfica por país y ciudad, sesiones rotativas o persistentes) y la Web Render API (páginas renderizadas en formato Markdown limpio) resuelven ambos problemas: la reputación de la IP y la segmentación geográfica, que le permiten sortear los bloqueos, y la representación, que le proporciona precios cargados mediante JavaScript sin necesidad de ejecutar navegadores por su cuenta. Descubra la Web Render API de Massive y sus proxies residenciales.

Fuentes

Preguntas frecuentes

¿Cuál es la forma más sencilla de empezar a extraer precios en Python?+

Instalar httpx y selectolax, envíe una solicitud GET con un encabezado User-Agent realista y seleccione el precio mediante un único selector CSS. Así se obtiene un rastreador funcional en unas 15 líneas. Añada proxies y reintentos en cuanto empiece a ser bloqueado, algo que, en sitios web de venta al por menor reales, suele ocurrir rápidamente.

¿Por qué aparece un precio diferente (o no aparece ningún precio) al que veo en mi navegador?+

Hay dos causas habituales. En primer lugar, es posible que el precio se genere mediante JavaScript, por lo que no figura en el código HTML sin procesar que recibe su cliente HTTP; necesitará un navegador sin interfaz gráfica o un servicio de conversión a Markdown. En segundo lugar, es posible que el sitio web utilice geo-cloaking, mostrando precios diferentes según el país, o que bloquee su dirección IP. Al extraer los datos a través de una IP residencial del propio país, se muestra el precio localizado correcto y se evitan los bloqueos de IP de centros de datos.

¿Necesito servidores proxy para extraer precios?+

Si se trata de unas pocas páginas, no. A un volumen considerable, sí. Las páginas de precios están muy protegidas, y una sola IP de centro de datos que realice solicitudes repetidas se ve rápidamente limitada en el número de solicitudes o bloqueada. Los proxies residenciales rotativos distribuyen las solicitudes entre direcciones IP reales de consumidores y le permiten dirigirse a países específicos, lo cual es necesario cuando los precios varían según el mercado.

¿Cómo puedo extraer los precios que se cargan mediante JavaScript?+

Tres opciones, por orden de preferencia: localizar la API JSON a la que recurre la página (consulte la pestaña «Red» de DevTools), utilizar un navegador sin interfaz gráfica como Playwright, o emplear una Web Render API que devuelva la página completamente renderizada (el formato Markdown es el más fácil de analizar). Texto sin formato solicitudes/httpx por sí solo no puede ejecutar JavaScript.

¿Es legal la extracción de precios?+

La recopilación de datos sobre precios disponibles públicamente ha resistido los recursos interpuestos en virtud de la CFAA de EE. UU. (en particular, hiQ contra LinkedIn), pero eso no abarca las condiciones de uso, los derechos de autor ni las reclamaciones relacionadas con la privacidad, y la legislación varía según la jurisdicción. Limítese a los datos públicos y respete robots.txt y respete las condiciones de uso, aplique los límites de uso de forma respetuosa y consulte a un abogado para cualquier asunto de carácter comercial o a gran escala.