Использование solvethree в промежуточном обработчике загрузчика Scrapy

Автор: solvethree4 мин на чтение

Используйте асинхронный 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 только для явно помеченных запросов

Scrapy · решение только для явно помеченных запросов отправки форм
# 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. 1.

    Задайте правильное действие и поле токена

    Пример использует действие login и помещает solution.token в g-recaptcha-response. Измените их в соответствии с интеграцией целевого приложения. Поле site задачи определяется по имени хоста из TARGET_URL; задачи reCAPTCHA не принимают поля type и url. Если recaptchaType не указан, выбирается enterprise-v3.

  2. 2.

    Передайте существующие поля формы

    Паук должен заполнить solvethree_fields обязательными полями целевого адреса, включая актуальное значение CSRF. Middleware копирует эти поля и добавляет токен; он не загружает форму и не извлекает скрытые поля.

  3. 3.

    Сохраняйте согласованность сессии

    Загрузите исходную форму через тот же прокси и с теми параметрами идентификации браузера, которые требуются приложению. Передайте её ID cookiejar в метаданных помеченного запроса. Возвращаемый FormRequest сохраняет этот cookiejar и использует solution.userAgent и solution.secChUa, но пример не подготавливает и не исправляет ранее созданную сессию.

  4. 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 задачи или устаревший токен.

Запустите первую задачу.

Начните с $5 на балансе рабочего пространства.

Создать рабочее пространство
Использование solvethree в промежуточном обработчике загрузчика Scrapy · solvethree