feature!: добавлен обход claudflare
не кликает!
This commit is contained in:
1 parent
40e5d046a9
commit
f1ed6edc7a
12 files changed
+171
-64
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user