feature!: добавлен обход claudflare

не кликает!
This commit is contained in:
protokey committed 2026-09-22 18:56:50 +04:00
1 parent 40e5d046a9
commit f1ed6edc7a
12 files changed
+171 -64

No files matched your search

+71 -8
View File
@@ -59,13 +59,16 @@ iFrame with checkbox
</div>
"""
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)
@@ -74,15 +77,23 @@ 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 = {}
checkbox_selector = "иногда надо тыкнуть, сейчас этого нет"
widget_selector = "div.cf-turnstile[data-sitekey], div[data-sitekey].cf-turnstile"
marker_selector = info.detect_locator or "//input[@name='cf-turnstile-response']"
info.detect_locator = marker_selector
log.debug(f"ищем {marker_selector} успех если есть {info.success_locator}")
if await page.locator(info.detect_locator).count() == 0:
if not await page.query_selector(marker_selector):
log.debug("на странице нет маркера преграды")
if info.success_locator:
info.success = bool(await page.query_selector(info.success_locator))
@@ -96,19 +107,67 @@ class CloudflareDetector(Detector):
log.debug("Решение не требуется")
return
info.stage = Stage.INTERSTITIAL
return
if await page.query_selector(checkbox_selector):
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, просто
# <div class="cf-turnstile" data-sitekey="...">) — решаем через
# 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:
raise NotImplementedError
iframe = page.frame(url=__CF_PATTERN__)
if iframe:
frame_el = await iframe.frame_element()
box = await frame_el.bounding_box()
else:
box = await page.locator("//input[@name='cf-turnstile-response]").bounding_box()
# Calculate the Captcha coordinates for any viewport
captcha_x, captcha_y = box["x"] + randint(26, 28), box["y"] + randint(25, 27)
# Move the mouse to the center of the window, then press and hold the left mouse button
# 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:
@@ -118,13 +177,17 @@ class CloudflareSolver(Solver):
else:
return
else:
deadline = time.time() + 30
deadline = time.time() + 10
while time.time() < deadline:
if not await page.query_selector(info.detect_locator):
return
return
else:
raise NotImplementedError
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