chore: настройка определения что мы на странице cf_turnstile

This commit is contained in:
protokey committed 2026-09-23 16:33:10 +04:00
1 parent 257310b7d5
commit 405e5ed2d0
1 file changed
+55 -4
+55 -4
View File
@@ -84,7 +84,7 @@ class CloudflareDetector(Detector):
async def detect(self, page: Page, info: ChallengeInfo): async def detect(self, page: Page, info: ChallengeInfo):
log.debug("CloudflareDetector run") log.debug("CloudflareDetector run")
await wait_for_networkidle(page) await wait_for_page_stability(page, True, False)
log.debug("Страница загружена полностью") log.debug("Страница загружена полностью")
info.meta = {} info.meta = {}
@@ -94,7 +94,9 @@ class CloudflareDetector(Detector):
log.debug(f"ищем {marker_selector} успех если есть {info.success_locator}") log.debug(f"ищем {marker_selector} успех если есть {info.success_locator}")
if not await page.query_selector(marker_selector): cf_type = await self._detect_cloudflare(page)
if not cf_type:
log.debug("на странице нет маркера преграды") log.debug("на странице нет маркера преграды")
if info.success_locator: if info.success_locator:
info.success = bool(await page.query_selector(info.success_locator)) info.success = bool(await page.query_selector(info.success_locator))
@@ -108,8 +110,6 @@ class CloudflareDetector(Detector):
log.debug("Решение не требуется") log.debug("Решение не требуется")
return 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 info.meta["cf_type"] = cf_type
log.debug(f"cType со страницы: {cf_type!r}") log.debug(f"cType со страницы: {cf_type!r}")
@@ -134,6 +134,41 @@ class CloudflareDetector(Detector):
else: else:
info.stage = Stage.INTERSTITIAL info.stage = Stage.INTERSTITIAL
@staticmethod
async def _detect_cloudflare(page: Page) -> str | None:
"""
Detect the type of Cloudflare challenge present in the provided page content.
This function analyzes the given page content to identify whether a specific
type of Cloudflare challenge is present. It checks for three predefined
challenge types: non-interactive, managed, and interactive. If a challenge
type is detected, it returns the corresponding type as a string. If no
challenge type is detected, it returns None.
Args:
page_content (str): The content of the page to analyze for Cloudflare
challenge types.
Returns:
str: A string representing the detected Cloudflare challenge type, if
found. Returns None if no challenge matches.
"""
challenge_types = (
"non-interactive",
"managed",
"interactive",
)
page_content = await page.content()
for ctype in challenge_types:
if f"cType: '{ctype}'" in page_content:
return ctype
# Check if turnstile captcha is embedded inside the page (Usually inside a closed Shadow iframe)
if await page.query_selector('script[src*="challenges.cloudflare.com/turnstile/v"]'):
return "embedded"
return None
__CF_PATTERN__ = re.compile(r"^https://challenges\.cloudflare\.com/cdn-cgi/challenge-platform/.*") __CF_PATTERN__ = re.compile(r"^https://challenges\.cloudflare\.com/cdn-cgi/challenge-platform/.*")
@@ -170,6 +205,7 @@ class CloudflareSolver(Solver):
while time.time() < deadline: while time.time() < deadline:
if not await page.query_selector(info.detect_locator): if not await page.query_selector(info.detect_locator):
return return
await page.wait_for_timeout(1)
return return
elif info.stage == Stage.INTERSTITIAL: elif info.stage == Stage.INTERSTITIAL:
if info.success_locator: if info.success_locator:
@@ -184,6 +220,7 @@ class CloudflareSolver(Solver):
while time.time() < deadline: while time.time() < deadline:
if not await page.query_selector(info.detect_locator): if not await page.query_selector(info.detect_locator):
return return
await page.wait_for_timeout(1)
return return
else: else:
@@ -193,6 +230,20 @@ class CloudflareSolver(Solver):
await captcha_solver.solve(info.meta["container_selector"], page) await captcha_solver.solve(info.meta["container_selector"], page)
# TODO: возбудить js # TODO: возбудить js
@classmethod
def _challenge_cleared(cls, page_content: str, challenge_type: str) -> bool:
"""
Check whether the Cloudflare challenge is no longer present in the page content.
Args:
page_content (str): The content of the page to analyze.
challenge_type (str): The challenge type returned by `_detect_cloudflare`.
Returns:
bool: True if the challenge is gone from the page, False otherwise.
"""
return challenge_type == "embedded" or cls._detect_cloudflare(page_content) is None
@register @register
class ReCaptchaProvider(AntiBotProvider): class ReCaptchaProvider(AntiBotProvider):