feature!: добавлен механизм обхода антибот защиты
This commit is contained in:
1 parent
f5871710ab
commit
1d2fdeae70
17 files changed
+735
-16
No files matched your search
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
абстракции: ChallengeInfo, Detector, Solver
|
||||
|
||||
Пользователь передает нам:
|
||||
|
||||
- селектор со страницы (достаточно общий чтобы покрыть весь сайт, но не страницу антибот защиты)
|
||||
- ключ сервиса распознавания капчи
|
||||
"""
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Type
|
||||
|
||||
from playwright.async_api import Page
|
||||
|
||||
|
||||
class Stage(str, Enum):
|
||||
INTERSTITIAL = "interstitial"
|
||||
CHECKBOX = "checkbox"
|
||||
INTERACTIVE = "interactive"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChallengeInfo:
|
||||
stage: Stage
|
||||
provider: str # "cloudflare_turnstile", "datadome", "hcaptcha", "recaptcha_v2", ...
|
||||
success: bool = False
|
||||
success_locator: str | None = None # локатор на странице за защитой
|
||||
site_key: str | None = None # для стадии C, чтобы передать в 2captcha
|
||||
meta: dict | None = None # что-то специфичное для провайдера
|
||||
|
||||
|
||||
class Solver:
|
||||
"""Алгоритм прохождения. Один солвер = один антибот провайдер"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._opt = kwargs
|
||||
|
||||
async def solve(self, page: Page, info: ChallengeInfo):
|
||||
"""Возвращает True, если стадия пройдена (или больше не требуется действий)."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Detector:
|
||||
"""Один детектор = одна сигнатура одного провайдера."""
|
||||
|
||||
async def detect(self, page: Page, info: ChallengeInfo):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AntiBotProvider:
|
||||
provider: str
|
||||
detector: Detector
|
||||
solver: Solver
|
||||
_solver_cls: Type[Solver]
|
||||
_detector_cls: Type[Detector]
|
||||
_success_locator: str | None = None
|
||||
|
||||
def __init__(self):
|
||||
self.detector = self._detector_cls()
|
||||
|
||||
async def detect(self, page: Page) -> ChallengeInfo:
|
||||
info = ChallengeInfo(
|
||||
stage = Stage.INTERSTITIAL,
|
||||
provider = self.provider,
|
||||
success_locator = self._success_locator,
|
||||
)
|
||||
# обновление info
|
||||
await self.detector.detect(page, info)
|
||||
return info
|
||||
|
||||
async def solve(self, page: Page, info: ChallengeInfo):
|
||||
await self.solver.solve(page, info)
|
||||
|
||||
def configure(self, success_locator=None, **kwargs):
|
||||
self.solver = self._solver_cls(**kwargs)
|
||||
self._success_locator = success_locator
|
||||
|
||||
def clear(self):
|
||||
del self.solver
|
||||
del self.detector
|
||||
Reference in new issue
Block a user