chore: настроен обход антибот защиты
This commit is contained in:
1 parent
4cc068a616
commit
1cfd9bafef
6 files changed
+118
-66
No files matched your search
+30
-30
@@ -16,36 +16,36 @@ from playwright.async_api import TimeoutError as PWTimeoutError
|
||||
|
||||
|
||||
async def click_and_race(page, element, challenge_locator, timeout=30_000):
|
||||
nav_task = asyncio.create_task(
|
||||
page.wait_for_event(
|
||||
"framenavigated",
|
||||
predicate=lambda frame: frame == page.main_frame,
|
||||
timeout=timeout,
|
||||
)
|
||||
)
|
||||
challenge_task = asyncio.create_task(
|
||||
challenge_locator.wait_for(state="attached", timeout=timeout)
|
||||
)
|
||||
nav_task = asyncio.create_task(
|
||||
page.wait_for_event(
|
||||
"framenavigated",
|
||||
predicate=lambda frame: frame == page.main_frame,
|
||||
timeout=timeout,
|
||||
)
|
||||
)
|
||||
challenge_task = asyncio.create_task(
|
||||
challenge_locator.wait_for(state="attached", timeout=timeout)
|
||||
)
|
||||
|
||||
await element.click()
|
||||
await element.click()
|
||||
|
||||
done, pending = await asyncio.wait(
|
||||
{nav_task, challenge_task}, return_when=asyncio.FIRST_COMPLETED
|
||||
)
|
||||
for t in pending:
|
||||
t.cancel()
|
||||
done, pending = await asyncio.wait(
|
||||
{nav_task, challenge_task}, return_when=asyncio.FIRST_COMPLETED
|
||||
)
|
||||
for t in pending:
|
||||
t.cancel()
|
||||
|
||||
if nav_task in done and nav_task.exception() is None:
|
||||
return "navigation"
|
||||
if challenge_task in done and challenge_task.exception() is None:
|
||||
return "challenge"
|
||||
if nav_task in done and nav_task.exception() is None:
|
||||
return "navigation"
|
||||
if challenge_task in done and challenge_task.exception() is None:
|
||||
return "challenge"
|
||||
|
||||
# оба протухли по таймауту / или упали с другой ошибкой
|
||||
for t in done:
|
||||
exc = t.exception()
|
||||
if exc and not isinstance(exc, PWTimeoutError):
|
||||
raise exc
|
||||
raise PWTimeoutError("Ни навигация, ни challenge-iframe не появились вовремя")
|
||||
# оба протухли по таймауту / или упали с другой ошибкой
|
||||
for t in done:
|
||||
exc = t.exception()
|
||||
if exc and not isinstance(exc, PWTimeoutError):
|
||||
raise exc
|
||||
raise PWTimeoutError("Ни навигация, ни challenge-iframe не появились вовремя")
|
||||
|
||||
|
||||
class Stage(str, Enum):
|
||||
@@ -97,10 +97,10 @@ class AntiBotProvider:
|
||||
|
||||
async def detect(self, page: Page) -> ChallengeInfo:
|
||||
info = ChallengeInfo(
|
||||
stage = Stage.INTERSTITIAL,
|
||||
provider = self.provider,
|
||||
success_locator = self._success_locator,
|
||||
detect_locator = self._detect_locator,
|
||||
stage=Stage.INTERSTITIAL,
|
||||
provider=self.provider,
|
||||
success_locator=self._success_locator,
|
||||
detect_locator=self._detect_locator,
|
||||
)
|
||||
# обновление info
|
||||
await self.detector.detect(page, info)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from playwright.async_api import Page
|
||||
@@ -23,13 +24,14 @@ class ReCaptchaSolver(Solver):
|
||||
async def solve(self, page: Page, info: ChallengeInfo):
|
||||
log.debug("Начинаю решение recaptcha антибот преграды")
|
||||
if info.stage == Stage.INTERSTITIAL:
|
||||
# TODO: вытащить
|
||||
await page.wait_for_timeout(15)
|
||||
return
|
||||
elif info.stage == Stage.CHECKBOX:
|
||||
log.debug("Нужно тыкнуть чекбокс")
|
||||
checkbox_selector = "#recaptcha-anchor"
|
||||
element = page.frame_locator("iframe[title='reCAPTCHA']").locator(checkbox_selector)
|
||||
interactive_marker_selector = "xpath=//div[@class='g-recaptcha-bubble-arrow'][1]"
|
||||
interactive_marker_selector = "//div[@class='g-recaptcha-bubble-arrow']/..//iframe"
|
||||
challenge_locator = page.locator(interactive_marker_selector)
|
||||
result = await click_and_race(page, element, challenge_locator)
|
||||
log.debug(f"Ожидание завершено: {result}")
|
||||
@@ -48,24 +50,29 @@ class ReCaptchaSolver(Solver):
|
||||
log.debug("Капча решена. Возбуждаем JS")
|
||||
try:
|
||||
async with page.expect_navigation(wait_until='load'):
|
||||
await page.evaluate("""
|
||||
try {
|
||||
const clients = window.___grecaptcha_cfg && window.___grecaptcha_cfg.clients;
|
||||
for (const cid in clients) {
|
||||
for (const key in clients[cid]) {
|
||||
const obj = clients[cid][key];
|
||||
for (const prop in obj) {
|
||||
const cb = obj[prop] && obj[prop].callback;
|
||||
if (typeof cb === 'function') { cb(token); return; }
|
||||
if (typeof cb === 'string' && typeof window[cb] === 'function') {
|
||||
window[cb](token); return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {}""", token)
|
||||
js = """(token) => {
|
||||
const clients = window.___grecaptcha_cfg && window.___grecaptcha_cfg.clients;
|
||||
if (!clients) return false;
|
||||
for (const cid in clients) {
|
||||
for (const key in clients[cid]) {
|
||||
const obj = clients[cid][key];
|
||||
for (const prop in obj) {
|
||||
const cb = obj[prop] && obj[prop].callback;
|
||||
if (typeof cb === 'function') { cb(token); return true; }
|
||||
if (typeof cb === 'string' && typeof window[cb] === 'function') {
|
||||
window[cb](token); return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}"""
|
||||
found = await page.evaluate(f"mw:({js})({json.dumps(token)})")
|
||||
assert found, "Recaptcha callback function not found"
|
||||
except Exception as e:
|
||||
log.warning("JS navigation error", exc_info=True)
|
||||
else:
|
||||
self.captcha_solver.recaptcha._clear_cache()
|
||||
log.debug("Все готово. Проверяем еще раз.")
|
||||
return
|
||||
|
||||
|
||||
Reference in new issue
Block a user