""" //input[@name="cf-turnstile-response"] //footer/footer-inner/footer-wrapper/div[@class="ray-id"] iFrame with checkbox
Значок zkillboard.com

zkillboard.com

Выполнение проверки безопасности

Этот веб-сайт использует сервис безопасности для защиты от вредоносных ботов. Эта страница отображается, пока веб-сайт проверяет, что вы не бот.

""" import logging import re import time from random import randint from playwright.async_api import Page from ..base import Detector, ChallengeInfo, AntiBotProvider, Solver from ..base import Stage from ..registry import register from ..humanize import wait_for_page_stability, wait_for_networkidle log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) log.addHandler(logging.StreamHandler()) class CloudflareDetector(Detector): # степени риска, которые Cloudflare сам проставляет в конфиге своего # челлендж-скрипта на странице (см. `cType: '...'` в html/js) — # по нему и определяем стадию, а не по факту наличия виджета на глаз _CTYPES = ("non-interactive", "managed", "interactive") async def detect(self, page: Page, info: ChallengeInfo): log.debug("CloudflareDetector run") await wait_for_page_stability(page, True, True) info.meta = {} widget_selector = "div.cf-turnstile[data-sitekey], div[data-sitekey].cf-turnstile" marker_selector = info.detect_locator or "xpath=//input[@name='cf-turnstile-response']" info.detect_locator = marker_selector log.debug(f"ищем {marker_selector} успех если есть {info.success_locator}") if not await page.query_selector(marker_selector): log.debug("на странице нет маркера преграды") if info.success_locator: info.success = bool(await page.query_selector(info.success_locator)) info.meta["reason"] = [ "Нет маркера блокировки и нет маркера успеха :-(", "Нет маркера блокировки зато есть маркер успеха :-)" ][info.success] else: info.success = True info.meta["reason"] = "нет маркера блокировки" log.debug("Решение не требуется") return content = await page.content() cf_type = next((t for t in self._CTYPES if f"cType: '{t}'" in content), None) info.meta["cf_type"] = cf_type log.debug(f"cType со страницы: {cf_type!r}") widget = await page.query_selector(widget_selector) if cf_type == "non-interactive": # фулл-пейдж интерстишл без чекбокса — просто ждать, пока пропадёт # или эскалирует в managed/interactive (тогда cf_type сменится на # следующем detect()) info.stage = Stage.INTERSTITIAL elif cf_type in ("managed", "interactive"): # CF уже решил, что нужен клик по виджету внутри своего iframe # (challenges.cloudflare.com) — сам клик этот проект не решает info.stage = Stage.CHECKBOX info.meta["requires_click"] = True elif widget: # обычный встроенный виджет на странице (без cType, просто #
) — решаем через # captcha_solver по токену, см. CloudflareSolver info.stage = Stage.CHECKBOX info.meta["container_selector"] = widget_selector else: info.stage = Stage.INTERSTITIAL __CF_PATTERN__ = re.compile(r"^https://challenges\.cloudflare\.com/cdn-cgi/challenge-platform/.*") class CloudflareSolver(Solver): async def solve(self, page: Page, info: ChallengeInfo): if info.stage == Stage.CHECKBOX: # # Calculate the Captcha coordinates for any viewport box = await page.locator("xpath=//input[@name='cf-turnstile-response']/../div").bounding_box() captcha_x, captcha_y = box["x"] + randint(26, 28), box["y"] + randint(25, 27) # # TODO: не кликает! await page.mouse.click(captcha_x, captcha_y, delay=randint(100, 200), button="left") await wait_for_networkidle(page) if info.success_locator: try: await page.wait_for_selector(info.success_locator) except: pass finally: return else: deadline = time.time() + 10 while time.time() < deadline: if not await page.query_selector(info.detect_locator): return return elif info.stage == Stage.INTERSTITIAL: if info.success_locator: try: await page.wait_for_selector(info.success_locator) except Exception: return else: return else: deadline = time.time() + 10 while time.time() < deadline: if not await page.query_selector(info.detect_locator): return return else: captcha_solver = self._opt.get("captcha_solver") if not captcha_solver: raise RuntimeError("Нужен captcha_solver (2captcha) чтобы решить Turnstile") await captcha_solver.solve(info.meta["container_selector"], page) # TODO: возбудить js @register class ReCaptchaProvider(AntiBotProvider): provider = 'cloudflare' _solver_cls = CloudflareSolver _detector_cls = CloudflareDetector