MechanicalSoup: Automatizar el raspado basado en formularios en Python
Senior Web Scraping Engineer
Resumen:
- MechanicalSoup automatiza sitios impulsados por formularios al combinar
requestscon BeautifulSoup. Un objetoStatefulBrowserabre una página, selecciona un formulario, llena sus campos, lo envía y analiza la respuesta: sin proceso de navegador y sin motor JavaScript. - El navegador mantiene las cookies y el estado de la sesión a través de las solicitudes por ti. Después de un inicio de sesión o un
Set-Cookie, el mismoStatefulBrowserenvía la cookie almacenada en cada solicitud posterior, por lo que los flujos de múltiples pasos se comportan como una sesión real. select_formmás la asignación de campo estilo diccionario es la totalidad de la superficie API. Seleccionas un formulario mediante un selector CSS, configurasbrowser["nombre_del_campo"] = valor, y llamas asubmit_selected()— la biblioteca maneja la codificación y la redirección.- MechanicalSoup solo lee el HTML que devuelve el servidor — no ejecuta JavaScript. Una página que construye su contenido del lado del cliente regresa vacía, ya que no hay un paso de renderizado DOM entre la respuesta HTTP y BeautifulSoup.
- Cuando una página necesita renderizado o activa un desafío anti-bot, entrega la carga a Scrapeless Scraping Browser y continúa analizando con BeautifulSoup. El SDK de Scrapeless genera una sesión en la nube, Playwright la controla a través de CDP para ejecutar el JavaScript, y el HTML renderizado fluye directamente de vuelta a los mismos selectores
soup.select(...). - Gratis para empezar. Las nuevas cuentas de Scrapeless incluyen tiempo de ejecución gratis de Scraping Browser — regístrate en app.scrapeless.com.
Introducción: los formularios siguen siendo donde se oculta la mayor parte del trabajo de scraping
Una gran parte de datos útiles se encuentra detrás de un formulario: un cuadro de búsqueda, un inicio de sesión, un panel de filtros, un asistente de múltiples páginas. MechanicalSoup existe precisamente para ese tipo de sitio. Envuelve la biblioteca HTTP requests y BeautifulSoup en un solo objeto de navegador con estado: obtiene una página, te permite llenar y enviar formularios HTML en ella, sigue la redirección y analiza lo que regresa. Sin Selenium, sin Chromium, sin binario de controlador.
El minimalismo lo mantiene rápido y también establece una línea clara sobre lo que la biblioteca puede alcanzar. MechanicalSoup habla semántica HTTP y analiza HTML. Nunca ejecuta el JavaScript de la página, por lo que cualquier cosa renderizada del lado del cliente —un feed de desplazamiento infinito, una lista de resultados de React, un cuadro de búsqueda que obtiene resultados a través de XHR— regresa como la concha vacía que el servidor envió primero. Se mantiene rápido en un formulario renderizado por el servidor y se vuelve ciego en uno renderizado por el cliente.
Esta guía recorre un flujo de trabajo real de MechanicalSoup de principio a fin: instalar, abrir una página, llenar y enviar un formulario, llevar cookies a través de una sesión y raspar los resultados, y luego muestra el límite honesto. Cuando un objetivo se renderiza en el navegador o está detrás de un desafío anti-bot activo, la carga se mueve a Scrapeless Scraping Browser a través del Protocolo DevTools de Chrome, mientras tu código de análisis BeautifulSoup permanece exactamente como estaba.
Lo que puedes hacer con ello
- Enviar formularios de inicio de sesión y mantener la autenticación. Completa los campos de nombre de usuario y contraseña, envía, y el
StatefulBrowsermantiene las cookies de sesión para cada página después de ello. - Conducir formularios de búsqueda y filtro. Establece un campo de consulta, envía y analiza las filas de resultados que devuelve el servidor: el clásico bucle de búsqueda y raspado.
- Recorrer flujos de múltiples páginas. Sigue enlaces y envía formularios sucesivos en un solo objeto de navegador, con la jarra de cookies y el referer llevados automáticamente.
- Leer tablas y listas renderizadas por el servidor. Cualquier cosa presente en el HTML en bruto —tablas de precios, listados, páginas de directorio— está a un
soup.select()de distancia. - Scripting de envíos repetitivos. Vuelve a ejecutar el mismo formulario con diferentes valores de campo para barrer un catálogo de consultas sin tocar un navegador real.
Por qué Scrapeless Scraping Browser
Scrapeless Scraping Browser es un navegador en la nube personalizable y anti-detección diseñado para rastreadores web y agentes de IA. Para las páginas que MechanicalSoup no puede alcanzar por sí solo, trae:
- Renderizado de JavaScript del lado de la nube — los scripts de la página se ejecutan en el navegador remoto, así que el contenido construido por el cliente existe en el HTML para cuando lo analices.
- Proxies residenciales en más de 195 países — ancla la salida con
proxy_countrypara que las páginas geográficamente restringidas y los formularios bloqueados por región sirvan el contenido que ofrecerían a un visitante local. - Huella anti-detección — la sesión se presenta como un navegador real, por lo que el formulario o página de resultados se renderiza en lugar de devolver un intersticial de desafío.
- Persistencia de sesión — las cookies y el estado de autenticación permanecen activos a través de las navegaciones dentro de una sesión, la misma propiedad que MechanicalSoup te ofrece localmente.
- Un endpoint CDP estándar —
browser_ws_endpointes una URL WebSocket ordinaria, por lo que Playwright (o cualquier cliente CDP) se conecta con una sola llamada y tu código de análisis permanece sin cambios.
Obtén tu clave API en el plan gratuito en app.scrapeless.com.
Requisitos previos
- Python 3.10 o superior
- Una cuenta de Scrapeless y clave API: regístrate en app.scrapeless.com (solo necesaria para la sección del navegador en la nube)
- Familiaridad básica con selectores CSS y la terminal
Instalación
MechanicalSoup es un paquete único que incluye requests y beautifulsoup4 como dependencias:
bash
pip install mechanicalsoup
Confirma la instalación y la versión:
bash
python -c "import mechanicalsoup; print(mechanicalsoup.__version__)"
# 1.4.0
Configuración: abrir una página con un StatefulBrowser
Todo en MechanicalSoup se ejecuta a través de un StatefulBrowser. Mantiene la página actual, la sesión y la jarra de cookies. Establece un agente de usuario al crear el objeto para que las solicitudes lleven una identidad razonable:
python
import mechanicalsoup
browser = mechanicalsoup.StatefulBrowser(
user_agent="Mozilla/5.0 (compatible; data-collector)"
)
browser.open("https://httpbingo.org/forms/post")
# browser.page es ahora un objeto BeautifulSoup que puedes consultar directamente
browser.open() devuelve la respuesta de requests; browser.page es el árbol BeautifulSoup analizado para la página que acabas de cargar.
Implementación básica: llenar y enviar un formulario
El bucle central son tres llamadas: seleccionar el formulario, asignar sus campos, enviar. select_form toma un selector CSS; la asignación de campos se realiza al estilo diccionario en el navegador; submit_selected() envía el formulario y sigue la redirección.
python
import mechanicalsoup
browser = mechanicalsoup.StatefulBrowser(
user_agent="Mozilla/5.0 (compatible; data-collector)"
)
browser.open("https://httpbingo.org/forms/post")
# Apunta al formulario por su acción, luego llena los campos por nombre
browser.select_form('form[action="/post"]')
browser["custname"] = "Ada Lovelace"
browser["custtel"] = "555-0100"
browser["custemail"] = "ada@example.com"
browser["size"] = "mediano" # botón de opción
browser["topping"] = ["bacon", "queso"] # casillas de verificación de múltiples valores
browser["comments"] = "Dejar en la puerta"
response = browser.submit_selected()
print(response.status_code)
data = response.json()
print(data["url"])
print(data["form"])
El endpoint de httpbin devuelve el cuerpo del formulario analizado, lo que confirma exactamente lo que MechanicalSoup envió:
json
{
"url": "https://httpbingo.org/post",
"form": {
"comments": "Dejar en la puerta",
"custemail": "ada@example.com",
"custname": "Ada Lovelace",
"custtel": "555-0100",
"delivery": "",
"size": "mediano",
"topping": ["bacon", "queso"]
}
}
// Los valores reflejan la verdadera presentación; el campo "delivery" vacío es un campo no establecido en el formulario.
Los botones de opción toman una sola cadena, los grupos de casillas de verificación toman una lista, y cualquier campo que quede sin configurar se envía vacío: la misma codificación que produciría un navegador.
Patrones avanzados
Mantener cookies a lo largo de una sesión
Un StatefulBrowser reutiliza una requests.Session, por lo que cualquier cookie que el servidor establezca persiste a solicitudes posteriores automáticamente. Eso es lo que hace que los inicios de sesión y los flujos de varios pasos funcionen:
python
import mechanicalsoup
browser = mechanicalsoup.StatefulBrowser()
# El servidor establece una cookie en esta solicitud
browser.open("https://httpbingo.org/cookies/set?session_id=abc123")
print(browser.session.cookies.get_dict())
# {'session_id': 'abc123'}
# Una solicitud posterior en el mismo navegador envía la cookie almacenada de vuelta
echo = browser.open("https://httpbingo.org/cookies")
print(echo.json())
# {'cookies': {'session_id': 'abc123'}}
Para un inicio de sesión real, primero envía el formulario de inicio de sesión, luego sigue usando el mismo objeto browser: la cookie de autenticación viaja en cada página subsiguiente.
Enviar un formulario de búsqueda y raspar los resultados
Un formulario de búsqueda GET sigue el mismo patrón: establece el campo de consulta, envía, analiza las filas de resultado de browser.page.
python
import mechanicalsoup
browser = mechanicalsoup.StatefulBrowser(
user_agent="Mozilla/5.0 (compatible; data-collector)"
)
browser.open("https://www.scrapethissite.com/pages/forms/")
browser.select_form('form[action="/pages/forms/"]')
browser["q"] = "boston"
browser.submit_selected()
print(browser.url) # https://www.scrapethissite.com/pages/forms/?q=boston
rows = browser.page.select("table.table tr.team")
print(f"{len(rows)} filas")
for row in rows[:3]:
name = row.select_one(".name").get_text(strip=True)
year = row.select_one(".year").get_text(strip=True)
wins = row.select_one(".wins").get_text(strip=True)
print(name, year, "victorias:", wins)
Debido a que la página de resultados se renderiza en el servidor, las filas están en el HTML que MechanicalSoup ya tiene, no se necesita una segunda solicitud.
Consigue tu clave de API en el plan gratuito: app.scrapeless.com
Donde MechanicalSoup se detiene: páginas que se renderizan en el navegador
MechanicalSoup le entrega a BeautifulSoup lo que el servidor devolvió por HTTP, nada más. Cuando una página construye su contenido con JavaScript del lado del cliente, ese HTML sin procesar es un cuerpo vacío, y los selectores no encuentran nada:
python
import mechanicalsoup
browser = mechanicalsoup.StatefulBrowser()
browser.open("https://quotes.toscrape.com/js/")
quotes = browser.page.select(".quote .text")
print("Citas encontradas por MechanicalSoup:", len(quotes))
# Citas encontradas por MechanicalSoup: 0
Cero. La variante /js/ de esa página inyecta sus citas con JavaScript después de cargar, por lo que no hay nada en el HTML del servidor para que BeautifulSoup lo coincida. El mismo obstáculo aparece frente a las páginas que tienen un desafío contra bots o que solo sirven contenido a una IP residencial, ninguno de los cuales un cliente solo HTTP puede superar.
La solución mantiene todo lo que ya escribiste. Deja que Scrapeless Scraping Browser haga el renderizado: el SDK crea una sesión en la nube, Playwright se conecta a ella a través de CDP y ejecuta el JavaScript de la página, y tú pasas el HTML renderizado directamente a los mismos selectores de BeautifulSoup.
Instala el SDK y un cliente de Playwright, luego obtiene el binario del navegador que Playwright controla:
bash
pip install scrapeless playwright beautifulsoup4
python -m playwright install chromium
Establece tu clave en el entorno, nunca la codifiques:
bash
export SCRAPELESS_API_KEY="tu_token_api_aqui"
Ahora renderiza la página del lado de la nube y analiza el resultado localmente:
python
import os
from scrapeless import Scrapeless
from scrapeless.types import ICreateBrowser
from playwright.sync_api import sync_playwright
from bs4 import BeautifulSoup
client = Scrapeless() # lee SCRAPELESS_API_KEY del entorno
# Crea una sesión en la nube; fija la salida residencial de EE. UU. para páginas con restricciones geográficas
session = client.browser.create(ICreateBrowser(
session_name="guía-mechanicalsoup",
session_ttl=180,
proxy_country="US",
))
with sync_playwright() as p:
# browser_ws_endpoint es una URL estándar wss:// CDP
browser = p.chromium.connect_over_cdp(session.browser_ws_endpoint)
page = browser.contexts[0].pages[0]
page.goto("https://quotes.toscrape.com/js/", wait_until="domcontentloaded")
page.wait_for_selector(".quote .text")
html = page.content()
browser.close()
# El mismo análisis de BeautifulSoup que usaste con MechanicalSoup
soup = BeautifulSoup(html, "html.parser")
quotes = soup.select(".quote .text")
print("Citas encontradas por Scrapeless + BeautifulSoup:", len(quotes))
print(quotes[0].get_text())
# Citas encontradas por Scrapeless + BeautifulSoup: 10
# “El mundo tal como lo hemos creado es un proceso de nuestro pensamiento. …”
La página que devolvió 0 filas a MechanicalSoup devuelve 10 a través del navegador en la nube, porque el JavaScript realmente se ejecutó antes de que se leyera el HTML. El renderizado y la salida se mueven del lado de la nube; la capa de análisis — soup.select(...) — es idéntica. Para una versión nativa de la biblioteca sobre la misma escalación, la guía del navegador en la nube Scrapling dirige un extractor de selector adaptativo a través del mismo browser_ws_endpoint.
Solución de problemas
| Síntoma | Causa | Solución |
|---|---|---|
LinkNotFoundError en select_form |
El selector CSS no coincide con ningún formulario en la página | Imprime browser.page.select("form") y apunta al action/atributos reales |
| Los selectores de resultados devuelven una lista vacía | La página renderiza su contenido con JavaScript | Renderízalo del lado de la nube con el Scraping Browser, luego analiza el HTML devuelto |
| La envío ignora un campo | El campo es un radio/casilla de verificación que necesita una cadena o lista, no un valor simple | Asigna una sola cadena a los radios, una lista a los grupos de casillas de verificación |
| Una página con sesión iniciada actúa como si estuviera desconectada | Un StatefulBrowser nuevo (nuevo jar de cookies) por paso |
Reutiliza un objeto browser para que la cookie de sesión persista |
| La página devuelve un desafío en lugar de contenido | Reto activo contra bots o verificación de región en un cliente solo HTTP | Fija proxy_country y deja que la huella digital del navegador en la nube renderice la página real |
Conclusión: mantiene el analizador, cambia el fetch
MechanicalSoup es la herramienta adecuada para el gran conjunto de sitios que aún son HTML simple y formularios: abre una página, select_form, asigna campos, submit_selected(), y lee las filas con BeautifulSoup. El jar de cookies hace que los inicios de sesión y los flujos de múltiples pasos funcionen sin código adicional. Su única limitación es JavaScript: lee HTML, no lo renderiza. Cuando un objetivo se construye en el navegador o está detrás de un muro anti-bots, la solución más limpia es cambiar solo la obtención: crea una sesión de Scrapeless, renderiza la página a través de CDP y alimenta el HTML resultante en los mismos selectores. Cuando la página necesita un navegador sin cabeza completo con su propia salida de proxy, la guía de proxy de Puppeteer cubre el mismo patrón del lado de la nube, y la documentación de Scraping Browser documenta toda la superficie de CDP. Fija la salida de EE. UU. para páginas con restricciones geográficas, reutiliza una sesión a lo largo de los pasos y trata los campos ausentes como anulables.
¿Listo para construir tu pipeline de datos potenciado por IA?
Únete a nuestra comunidad para reclamar un plan gratuito y conectarte con desarrolladores que construyen automatización de formularios y pipelines de renderizado: Discord · Telegram.
Regístrate en app.scrapeless.com para obtener un tiempo de ejecución gratuito de Scraping Browser y adapta los patrones anteriores a los formularios, inicios de sesión y páginas renderizadas que necesita tu flujo de trabajo. Consulta precios para escalas.
FAQ
Q: ¿MechanicalSoup ejecuta JavaScript?
No. MechanicalSoup envuelve requests y BeautifulSoup, por lo que solo ve el HTML que devuelve el servidor. Las páginas que construyen su contenido en el lado del cliente regresan vacías; renderiza esas a través de un navegador en la nube y analiza el HTML resultante con los mismos selectores de BeautifulSoup.
Q: ¿Cómo maneja MechanicalSoup los inicios de sesión y las sesiones?
Un solo StatefulBrowser reutiliza una requests.Session, por lo que cualquier cookie que establezca el servidor persiste en cada solicitud posterior automáticamente. Envía el formulario de inicio de sesión una vez, luego sigue usando el mismo objeto del navegador y la cookie de autenticación permanece.
Q: ¿Cómo selecciono un formulario específico en una página?
Pasa un selector CSS a select_form, por ejemplo browser.select_form('form[action="/post"]'). Si no hay un formulario que coincida, obtendrás un LinkNotFoundError: imprime browser.page.select("form") para ver los atributos reales y dirigirte a uno de ellos.
Q: ¿Es legal raspar un sitio con MechanicalSoup?
Raspar datos públicamente visibles es generalmente permisible, pero las reglas varían según la jurisdicción y los términos de servicio del sitio. Revisa los Términos de Servicio del objetivo, respeta las directrices de robots, evita datos personales o restringidos y consulta a un abogado para cualquier cosa ambigua.
Q: ¿Necesito un proxy con MechanicalSoup?
Para páginas abiertas, renderizadas por el servidor, a menudo no. Para páginas que restringen por región o que solo sirven contenido a IPs residenciales, dirige la obtención a través del Scraping Browser de Scrapeless y fija proxy_country para que la solicitud salga desde una IP en la que el sitio confíe.
Q: ¿Puedo mantener mi código de BeautifulSoup cuando me mude al navegador en la nube?
Sí. El navegador en la nube solo reemplaza el paso de obtención; devuelve HTML renderizado, que puedes analizar con las mismas llamadas soup.select(...) que usabas con MechanicalSoup. La capa de análisis no cambia.
En Scrapeless, solo accedemos a datos disponibles públicamente y cumplimos estrictamente con las leyes, regulaciones y políticas de privacidad del sitio web aplicables. El contenido de este blog es sólo para fines de demostración y no implica ninguna actividad ilegal o infractora. No ofrecemos garantías y renunciamos a toda responsabilidad por el uso de la información de este blog o enlaces de terceros. Antes de realizar cualquier actividad de scraping, consulte a su asesor legal y revise los términos de servicio del sitio web de destino u obtenga los permisos necesarios.



