Utiliser solvethree dans un middleware de téléchargement Scrapy
Utilisez un middleware de téléchargement asynchrone pour ne résoudre que les requêtes explicitement marquées par votre spider. Exécutez le SDK solvethree synchrone dans un thread de travail, puis renvoyez une FormRequest contenant le jeton, le proxy et l'identité de navigateur renvoyée.
L'exemple gère un seul point de terminaison de formulaire reCAPTCHA Enterprise configuré. Il ne détecte pas les défis et ne détermine pas comment un site web quelconque attend son jeton.
Sur cette page
Configurer la cible avant d'activer le middleware
- Utilisez Scrapy 2.19+, Python 3.10+ et le SDK Python officiel de solvethree. Le SDK est synchrone ; il ne dispose pas de client asynchrone natif.
- Créez un profil de site actif pour votre espace de travail dans la console. Envoyer un nom d'hôte et une action n'en crée pas. Consultez la configuration du profil de site.
- Définissez TARGET_URL sur l'URL exacte d'envoi du formulaire, SOLVETHREE_PROXY sur le proxy utilisé pour cette requête de l'application, et SOLVETHREE_API_KEY sur la clé de votre espace de travail. Conservez la clé côté serveur.
- Activez AsyncioSelectorReactor et enregistrez le middleware avec la priorité 740, comme indiqué ci-dessous. Il sera ainsi placé avant HttpProxyMiddleware, dont la priorité est 750.
Résoudre uniquement les requêtes explicitement marquées
# 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.La méthode process_request du middleware accepte uniquement request, sans l'argument spider obsolète. Les requêtes non marquées passent sans modification. Une requête marquée doit correspondre exactement à TARGET_URL ; une différence déclenche une erreur au lieu d'envoyer un jeton à un autre point de terminaison.
asyncio.to_thread exécute les appels réseau et les interrogations périodiques du SDK synchrone hors du thread de la boucle d'événements. Déclarer simplement process_request comme async ne suffirait pas. Le SDK crée une tâche et attend un résultat prêt ou lève une exception ; le middleware construit ensuite l'envoi du formulaire.
Respecter le formulaire réellement envoyé par votre application
- 1.
Définir l'action et le champ de jeton appropriés
L'exemple utilise l'action login et place solution.token dans g-recaptcha-response. Modifiez ces valeurs pour qu'elles correspondent à l'intégration de la cible. Le champ site de la tâche est dérivé du nom d'hôte de TARGET_URL ; les tâches reCAPTCHA n'acceptent pas les champs type ou url. Omettre recaptchaType sélectionne enterprise-v3.
- 2.
Fournir les champs de formulaire existants
Le spider doit renseigner solvethree_fields avec les champs requis par le point de terminaison, y compris sa valeur CSRF actuelle. Le middleware copie ces champs et ajoute le jeton ; il ne charge pas le formulaire et n'extrait pas les champs masqués.
- 3.
Maintenir la cohérence de la session
Chargez le formulaire initial avec le même proxy et la même identité de navigateur que ceux requis par l'application. Transmettez son identifiant cookiejar dans les métadonnées de la requête marquée. La FormRequest renvoyée conserve ce cookiejar et utilise solution.userAgent et solution.secChUa, mais l'exemple ne prépare ni ne répare la session antérieure.
- 4.
Traiter la réponse de l'application
Fournissez un callback qui vérifie si l'envoi du formulaire a réussi et un errback pour les échecs de requête. Une tâche prête signifie qu'une solution a été générée, et non que le site web a accepté la requête suivante.
Cet exemple utilise volontairement un seul proxy pour la résolution et l'envoi. Ne le remplacez pas par un proxy sans rapport après la résolution. Inspectez également tout middleware de téléchargement personnalisé qui réécrit User-Agent ou les indications client : il ne doit pas écraser l'identité renvoyée avec la solution.
Éviter qu'une nouvelle tentative déclenche une autre résolution
Enregistrez solvethree_request_id avant de transmettre la requête marquée avec yield, et conservez-le pour cette même résolution logique. Un requestId accepte 1–64 lettres ASCII, chiffres, traits de soulignement ou traits d'union. Le réutiliser avec des données d'entrée de tâche identiques permet de récupérer la tâche existante ; modifier ces données tout en conservant l'identifiant provoque un conflit.
La FormRequest renvoyée définit solvethree_applied pour ne pas déclencher une autre résolution. Elle définit également dont_retry et dont_redirect, ce qui empêche les nouvelles tentatives et les redirections automatiques pour cet envoi. Ces indicateurs n'empêchent pas votre propre callback ou votre exécuteur de tâches de lancer une autre requête : prenez donc cette décision explicitement.
- L'appel de résolution dépasse le délai d'attente.
Déterminez si une tâche a déjà été créée. Un dépassement du délai de connexion ne prouve pas que la création a échoué.
Essayez ceci : Récupérez la tâche connue ou retrouvez-la à l’aide du requestId enregistré et de paramètres de tâche identiques avant de créer une nouvelle demande logique de résolution.
- La résolution lève une exception au lieu de renvoyer un résultat.
Distinguez une erreur de requête HTTP d’un échec définitif de tâche. Pour une tâche en échec, examinez errorCode et error.
Essayez ceci : Gérez l’échec au lieu de renvoyer sans cesse la même requête marquée avec yield. Corrigez les paramètres invalides avant de réessayer ; les tâches en échec ne doivent plus être interrogées.
- Le site web rejette le formulaire envoyé.
Examinez d’abord le champ du jeton envoyé, les champs CSRF, les cookies, le proxy et l’identité du navigateur.
Essayez ceci : Corrigez la requête de l’application avant de payer une nouvelle résolution. Le champ doit contenir solution.token, et non l’ID de la tâche ou un jeton périmé.
Sources et référence de l’API
Lancez votre première tâche.
Démarrez avec $5 de crédit dans votre espace de travail.