Usa solvethree en un middleware de descarga de Scrapy
Usa un middleware de descarga asíncrono para resolver solo las solicitudes que tu spider marque explícitamente. Ejecuta el SDK síncrono de solvethree en un hilo de trabajo y devuelve después una FormRequest que contenga el token, el proxy y la identidad de navegador devuelta.
El ejemplo gestiona un único endpoint de formulario de reCAPTCHA Enterprise configurado. No detecta desafíos ni descubre cómo espera recibir el token un sitio web cualquiera.
En esta página
Configura el destino antes de habilitar el middleware
- Usa Scrapy 2.19+, Python 3.10+ y el SDK oficial de solvethree para Python. El SDK es síncrono; no tiene un cliente asíncrono nativo.
- Crea en la consola un perfil de sitio activo para tu espacio de trabajo. Enviar un nombre de host y una acción no lo crea. Consulta la configuración del perfil de sitio.
- Establece TARGET_URL en la URL exacta de envío del formulario, SOLVETHREE_PROXY en el proxy usado para esta solicitud de la aplicación y SOLVETHREE_API_KEY en la clave de tu espacio de trabajo. Mantén la clave en el servidor.
- Habilita AsyncioSelectorReactor y registra el middleware con la prioridad 740, como se muestra a continuación. Esto lo sitúa antes de HttpProxyMiddleware, cuya prioridad es 750.
Resuelve solo las solicitudes marcadas explícitamente
# middlewares.py; Scrapy 2.19+, Python 3.10+, official solvethree SDK.
import asyncio
import os
from urllib.parse import urlsplit
from scrapy.http import FormRequest
from solvethree import Solvethree
class CaptchaMiddleware:
async def process_request(self, request):
if not request.meta.get("solvethree_form") or request.meta.get("solvethree_applied"):
return None
target = os.environ["TARGET_URL"]
if request.url != target:
raise ValueError("Only the configured form target may use this middleware")
proxy = os.environ["SOLVETHREE_PROXY"]
def solve():
return Solvethree(os.environ["SOLVETHREE_API_KEY"]).solve({
"site": urlsplit(target).hostname, "action": "login", "proxy": proxy,
"requestId": request.meta["solvethree_request_id"],
}, timeout=180)
result = await asyncio.to_thread(solve)
solution = result["solution"]
fields = dict(request.meta["solvethree_fields"])
fields["g-recaptcha-response"] = solution["token"]
meta = {**request.meta, "proxy": proxy, "solvethree_applied": True,
"dont_retry": True, "dont_redirect": True}
return FormRequest(target, formdata=fields, callback=request.callback,
errback=request.errback, headers={"User-Agent": solution["userAgent"],
"sec-ch-ua": solution["secChUa"]}, meta=meta, dont_filter=True)
# settings.py:
# TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
# DOWNLOADER_MIDDLEWARES = {"myproject.middlewares.CaptchaMiddleware": 740}
# The spider first loads its form/CSRF data with the same proxy and cookiejar.
# Then yield a Request(TARGET_URL, callback=your_result_handler, meta={
# "solvethree_form": True, "solvethree_request_id": one_saved_logical_job_id,
# "solvethree_fields": {"csrf": token_from_the_form, "email": "your-test-value"},
# "cookiejar": your_cookiejar_id,
# })
# Inspect the final response in your_result_handler; a ready task is not that response.El método process_request del middleware solo recibe request, sin el argumento obsoleto spider. Las solicitudes sin marcar pasan sin cambios. Una solicitud marcada debe coincidir exactamente con TARGET_URL; una discrepancia provoca un error en lugar de enviar un token a otro endpoint.
asyncio.to_thread mantiene las llamadas de red y las consultas periódicas del SDK síncrono fuera del hilo del bucle de eventos. Declarar process_request como async no bastaría para conseguirlo. El SDK crea una tarea y espera un resultado listo o lanza una excepción; después, el middleware construye el envío del formulario.
Adáptalo al formulario que realmente envía tu aplicación
- 1.
Configura la acción y el campo del token correctos
El ejemplo usa la acción login e introduce solution.token en g-recaptcha-response. Cambia esos valores para que coincidan con la integración del destino. El campo site de la tarea se obtiene del nombre de host de TARGET_URL; las tareas de reCAPTCHA no aceptan campos type ni url. Si se omite recaptchaType, se selecciona enterprise-v3.
- 2.
Proporciona los campos existentes del formulario
El spider debe rellenar solvethree_fields con los campos que requiere el endpoint, incluido su valor CSRF actual. El middleware copia esos campos y añade el token; no carga el formulario ni extrae campos ocultos.
- 3.
Mantén la coherencia de la sesión
Carga el formulario inicial a través del mismo proxy y con la misma identidad de navegador que requiere la aplicación. Pasa su ID de cookiejar en los metadatos de la solicitud marcada. La FormRequest devuelta conserva ese cookiejar y usa solution.userAgent y solution.secChUa, pero el ejemplo no prepara ni repara la sesión anterior.
- 4.
Gestiona la respuesta de la aplicación
Proporciona un callback que compruebe si el envío del formulario se completó correctamente y un errback para los fallos de las solicitudes. Una tarea lista significa que se ha generado una solución, no que el sitio web haya aceptado la solicitud posterior.
Este ejemplo usa deliberadamente un solo proxy para la resolución y el envío. No lo sustituyas por un proxy distinto después de la resolución. Revisa también cualquier middleware de descarga personalizado que reescriba User-Agent o las indicaciones de cliente: no debe sobrescribir la identidad devuelta con la solución.
Evita que los reintentos generen otra resolución
Guarda solvethree_request_id antes de ceder la solicitud marcada y consérvalo para esa resolución lógica. Un requestId admite entre 1 y 64 letras ASCII, números, guiones bajos o guiones. Reutilizarlo con datos de entrada idénticos para la tarea recupera la tarea existente; cambiar esos datos manteniendo el ID provoca un conflicto.
La FormRequest devuelta establece solvethree_applied para no activar otra resolución. También establece dont_retry y dont_redirect, lo que impide los reintentos y las redirecciones automáticos para ese envío. Esos indicadores no impiden que tu propio callback o ejecutor de trabajos inicie otra solicitud, así que toma esa decisión de forma explícita.
- La llamada de resolución agota el tiempo de espera.
Determina si ya se ha creado una tarea. Que una conexión agote el tiempo de espera no demuestra que la creación haya fallado.
Prueba esto: Recupera la tarea conocida o recupérala usando el requestId guardado y exactamente los mismos datos de entrada de la tarea antes de crear una nueva solicitud lógica de resolución.
- La resolución lanza una excepción en lugar de devolver un resultado.
Distingue un error de solicitud HTTP de un fallo terminal de la tarea. Si una tarea falla, revisa errorCode y error.
Prueba esto: Gestiona el fallo en lugar de seguir entregando la misma solicitud marcada. Corrige los datos de entrada no válidos antes de volver a intentarlo; no debes seguir consultando tareas fallidas.
- El sitio web rechaza el formulario enviado.
Revisa primero el campo del token enviado, los campos CSRF, las cookies, el proxy y la identidad del navegador.
Prueba esto: Corrige la solicitud de la aplicación antes de pagar por otra resolución. El campo debe contener solution.token, no el ID de la tarea ni un token obsoleto.
Fuentes y referencia de la API
Ejecuta tu primera tarea.
Empieza con $5 de crédito en tu espacio de trabajo.