Использование solvethree в промежуточном обработчике загрузчика Scrapy
Используйте асинхронный middleware загрузчика, чтобы решать CAPTCHA только для запросов, явно помеченных вашим пауком. Запускайте синхронный SDK solvethree в рабочем потоке, затем возвращайте FormRequest с токеном, прокси и полученными параметрами идентификации браузера.
Пример обрабатывает один настроенный адрес отправки формы reCAPTCHA Enterprise. Он не обнаруживает проверки CAPTCHA и не определяет, каким способом произвольный сайт ожидает получить токен.
На этой странице
Настройте целевой адрес перед включением middleware
- Используйте Scrapy 2.19+, Python 3.10+ и официальный Python SDK solvethree. SDK синхронный; встроенного асинхронного клиента у него нет.
- Создайте активный профиль сайта для своего рабочего пространства в консоли. Отправка имени хоста и действия не создаёт профиль. См. настройку профиля сайта.
- Задайте в TARGET_URL точный URL отправки формы, в SOLVETHREE_PROXY — прокси для этого запроса приложения, а в SOLVETHREE_API_KEY — ключ рабочего пространства. Храните ключ на стороне сервера.
- Включите AsyncioSelectorReactor и зарегистрируйте middleware с приоритетом 740, как показано ниже. Это размещает его перед HttpProxyMiddleware с приоритетом 750.
Решайте CAPTCHA только для явно помеченных запросов
# 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.Метод process_request middleware принимает только request, без устаревшего аргумента spider. Непомеченные запросы проходят без изменений. Помеченный запрос должен точно соответствовать TARGET_URL; при несовпадении возникает ошибка, а не отправка токена на другой адрес.
asyncio.to_thread выносит сетевые вызовы и опрос синхронного SDK из потока цикла событий. Простое объявление process_request асинхронным этого не обеспечило бы. SDK создаёт задачу и ждёт готового результата либо выбрасывает исключение; затем middleware формирует запрос отправки формы.
Воспроизведите форму, которую фактически отправляет приложение
- 1.
Задайте правильное действие и поле токена
Пример использует действие login и помещает solution.token в g-recaptcha-response. Измените их в соответствии с интеграцией целевого приложения. Поле site задачи определяется по имени хоста из TARGET_URL; задачи reCAPTCHA не принимают поля type и url. Если recaptchaType не указан, выбирается enterprise-v3.
- 2.
Передайте существующие поля формы
Паук должен заполнить solvethree_fields обязательными полями целевого адреса, включая актуальное значение CSRF. Middleware копирует эти поля и добавляет токен; он не загружает форму и не извлекает скрытые поля.
- 3.
Сохраняйте согласованность сессии
Загрузите исходную форму через тот же прокси и с теми параметрами идентификации браузера, которые требуются приложению. Передайте её ID cookiejar в метаданных помеченного запроса. Возвращаемый FormRequest сохраняет этот cookiejar и использует solution.userAgent и solution.secChUa, но пример не подготавливает и не исправляет ранее созданную сессию.
- 4.
Обработайте ответ приложения
Укажите callback, который проверяет успешность отправки формы, и errback для ошибок запросов. Готовая задача означает, что решение сгенерировано, а не что сайт принял последующий запрос.
В этом примере намеренно используется один прокси для решения и отправки. Не заменяйте его другим прокси после решения. Также проверьте все пользовательские middleware загрузчика, которые изменяют User-Agent или клиентские подсказки: они не должны перезаписывать параметры идентификации, полученные вместе с решением.
Не допускайте нового решения при повторных попытках
Сохраните solvethree_request_id перед выдачей помеченного запроса через yield и используйте его для этого логического решения. requestId допускает 1–64 ASCII-буквы, цифры, символы подчёркивания или дефисы. Повторное использование с идентичными входными данными задачи восстанавливает существующую задачу; изменение этих данных при сохранении ID вызывает конфликт.
Возвращаемый FormRequest устанавливает solvethree_applied, поэтому не запускает ещё одно решение. Он также устанавливает dont_retry и dont_redirect, предотвращая автоматические повторные попытки и перенаправления для этой отправки. Эти флаги не мешают вашему callback или исполнителю заданий начать другой запрос, поэтому принимайте это решение явно.
- Истекает время ожидания вызова решения.
Определите, была ли задача уже создана. Тайм-аут соединения не доказывает, что создание не удалось.
Попробуйте: Получите известную задачу или восстановите её с помощью сохранённого requestId и идентичных входных данных задачи, прежде чем создавать новый логический запрос на решение.
- При решении возникает исключение вместо возврата результата.
Различайте ошибку HTTP-запроса и окончательное завершение задачи с ошибкой. Для задачи, завершившейся с ошибкой, проверьте errorCode и error.
Попробуйте: Обработайте ошибку, а не передавайте повторно тот же помеченный запрос. Исправьте некорректные входные данные перед новой попыткой; опрос задач, завершившихся с ошибкой, необходимо прекратить.
- Сайт отклоняет отправленную форму.
Сначала проверьте отправляемое поле токена, поля CSRF, cookie, прокси и идентификационные данные браузера.
Попробуйте: Исправьте запрос приложения, прежде чем оплачивать ещё одно решение. Поле должно содержать solution.token, а не ID задачи или устаревший токен.
Источники и справочник API
Запустите первую задачу.
Начните с $5 на балансе рабочего пространства.