fix: из-за этого не дает пройти cf_turnstile
This commit is contained in:
1 parent
eefcf7a0a9
commit
51a801f612
2 files changed
-103
No files matched your search
@@ -1,80 +0,0 @@
|
|||||||
"""
|
|
||||||
Генерация и persist согласованного browser fingerprint на профиль (user_data_dir).
|
|
||||||
|
|
||||||
Используем browserforge — ту же библиотеку, которую под капотом использовал Camoufox — но
|
|
||||||
поверх голого Playwright Chromium: генерируем Fingerprint (navigator/screen/WebGL/canvas/шрифты/
|
|
||||||
headers, всё согласовано друг с другом и с выбранным UA) и инжектим его через add_init_script.
|
|
||||||
|
|
||||||
Важно: генерируем именно под browser=['chrome'] — раз движок у нас реальный Chromium, подделывать
|
|
||||||
navigator под Firefox/Safari было бы хуже, чем не подделывать вообще (несоответствие между
|
|
||||||
заявленным UA и реальными JS-фичами движка — сильный сигнал для антибота).
|
|
||||||
"""
|
|
||||||
import json
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from browserforge.fingerprints import Fingerprint, FingerprintGenerator, Screen
|
|
||||||
from browserforge.fingerprints.generator import NavigatorFingerprint, ScreenFingerprint, VideoCard
|
|
||||||
from browserforge.injectors.utils import InjectFunction, only_injectable_headers
|
|
||||||
|
|
||||||
FINGERPRINT_FILE = "fingerprint.json"
|
|
||||||
|
|
||||||
|
|
||||||
def _fingerprint_from_dict(data: dict) -> Fingerprint:
|
|
||||||
data = dict(data)
|
|
||||||
data["screen"] = ScreenFingerprint(**data["screen"])
|
|
||||||
data["navigator"] = NavigatorFingerprint(**data["navigator"])
|
|
||||||
if data.get("videoCard"):
|
|
||||||
data["videoCard"] = VideoCard(**data["videoCard"])
|
|
||||||
return Fingerprint(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def load_or_create_fingerprint(user_data_dir: Path, **generator_kwargs) -> Fingerprint:
|
|
||||||
"""Держит fingerprint стабильным для профиля: генерируется один раз при первом запросе
|
|
||||||
этой сессии (user_data_dir) и переиспользуется во всех последующих — так же, как реальный
|
|
||||||
человек не меняет видеокарту/шрифты/UA между визитами на сайт."""
|
|
||||||
fp_file = user_data_dir / FINGERPRINT_FILE
|
|
||||||
if fp_file.exists():
|
|
||||||
return _fingerprint_from_dict(json.loads(fp_file.read_text()))
|
|
||||||
|
|
||||||
generator_kwargs.setdefault("browser", ["chrome"])
|
|
||||||
# strict=True: без него browserforge при отсутствии точного совпадения в датасете
|
|
||||||
# тихо ослабляет даже нижнюю границу screen (min_width/min_height) — на практике
|
|
||||||
# так может прилететь мобильный экран под десктопный viewport (screen.availWidth
|
|
||||||
# меньше самого окна браузера — физически невозможно и легко детектится), а в
|
|
||||||
# части случаев цикл релаксации виснет на неопределённое время вместо быстрого
|
|
||||||
# ответа. Со strict=True generate() либо отдаёт честно подходящий сэмпл, либо
|
|
||||||
# сразу кидает ValueError — и то и другое быстро (~0.1с)
|
|
||||||
generator_kwargs.setdefault("strict", True)
|
|
||||||
fingerprint = FingerprintGenerator().generate(**generator_kwargs)
|
|
||||||
fp_file.write_text(fingerprint.dumps())
|
|
||||||
return fingerprint
|
|
||||||
|
|
||||||
|
|
||||||
def browser_kwargs_for(fingerprint: Fingerprint, browser_name: str = "chromium") -> dict:
|
|
||||||
"""kwargs для `AsyncStealthySession(**kwargs)`, полностью выставляющие браузер под fingerprint —
|
|
||||||
UA, заголовки, init-script (подделка navigator/screen/WebGL/codecs/battery) и размер окна
|
|
||||||
(viewport/screen/device_scale_factor).
|
|
||||||
|
|
||||||
Всё уходит обычными kwargs сессии — тем же путём, что и остальная конфигурация
|
|
||||||
(proxy/cookies/locale и т.д.), как это делает сама scrapling — а не отдельным вызовом после
|
|
||||||
запуска контекста (add_init_script/set_extra_http_headers руками требуют context, который
|
|
||||||
есть только после входа в `async with AsyncStealthySession(...)`, то есть после того, как
|
|
||||||
решение о конфигурации уже "должно было" быть принято до запуска браузера).
|
|
||||||
|
|
||||||
:param fingerprint: Сгенерированный/загруженный fingerprint (см. load_or_create_fingerprint).
|
|
||||||
:param browser_name: Имя браузера для фильтрации заголовков (only_injectable_headers) —
|
|
||||||
"chromium", раз движок у нас реальный Chromium (см. docstring модуля).
|
|
||||||
:return: dict с ключами `useragent`, `extra_headers`, `init_script_content`, `additional_args`
|
|
||||||
(viewport/screen/device_scale_factor) — распаковывается в kwargs AsyncStealthySession.
|
|
||||||
"""
|
|
||||||
screen = {"width": fingerprint.screen.width, "height": fingerprint.screen.height}
|
|
||||||
return {
|
|
||||||
"useragent": fingerprint.navigator.userAgent,
|
|
||||||
"extra_headers": only_injectable_headers(fingerprint.headers, browser_name),
|
|
||||||
"init_script_content": InjectFunction(fingerprint),
|
|
||||||
"additional_args": {
|
|
||||||
"viewport": dict(screen),
|
|
||||||
"screen": dict(screen),
|
|
||||||
"device_scale_factor": fingerprint.screen.devicePixelRatio,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
-23
@@ -6,7 +6,6 @@ from pathlib import Path
|
|||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
from browserforge.fingerprints import Screen
|
|
||||||
from fastapi import Cookie
|
from fastapi import Cookie
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
@@ -14,7 +13,6 @@ from fastapi import Response
|
|||||||
from patchright.async_api import Page, ProxySettings
|
from patchright.async_api import Page, ProxySettings
|
||||||
|
|
||||||
from api import actions
|
from api import actions
|
||||||
from api.fingerprint import browser_kwargs_for, load_or_create_fingerprint
|
|
||||||
from api.geoip import resolve_geo
|
from api.geoip import resolve_geo
|
||||||
from api.schemas import SolveRequest
|
from api.schemas import SolveRequest
|
||||||
from engine import AsyncStealthySession
|
from engine import AsyncStealthySession
|
||||||
@@ -115,27 +113,6 @@ async def solve(
|
|||||||
if locale:
|
if locale:
|
||||||
fp_kwargs["locale"] = locale
|
fp_kwargs["locale"] = locale
|
||||||
|
|
||||||
scr_size = (1920, 1080)
|
|
||||||
fingerprint = load_or_create_fingerprint(
|
|
||||||
user_data_dir,
|
|
||||||
# только нижняя граница: реальный монитор физически не может быть
|
|
||||||
# меньше открытого на нём окна браузера (viewport ниже берём из
|
|
||||||
# fingerprint'а, не отсюда)
|
|
||||||
screen=Screen(min_width=scr_size[0], min_height=scr_size[1]),
|
|
||||||
**fp_kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
# UA/заголовки/init-script (подделка navigator/screen/WebGL/...) /viewport/screen/
|
|
||||||
# device_scale_factor под fingerprint — единым куском kwargs (см.
|
|
||||||
# api/fingerprint.py:browser_kwargs_for), тем же путём, что и остальная конфигурация
|
|
||||||
# сессии (proxy/cookies/locale), а не отдельным вызовом после запуска контекста.
|
|
||||||
fp_browser_kwargs = browser_kwargs_for(fingerprint)
|
|
||||||
permissions = ["notifications"]
|
|
||||||
if geo and geo.get("geolocation"):
|
|
||||||
fp_browser_kwargs["additional_args"]["geolocation"] = geo["geolocation"]
|
|
||||||
permissions.append("geolocation")
|
|
||||||
fp_browser_kwargs["additional_args"]["permissions"] = permissions
|
|
||||||
|
|
||||||
nav_timeout_ms = payload.timeout * 1000 if payload.timeout else 60_000
|
nav_timeout_ms = payload.timeout * 1000 if payload.timeout else 60_000
|
||||||
blocked_domains = set(payload.blocked_domains) if payload.blocked_domains else None
|
blocked_domains = set(payload.blocked_domains) if payload.blocked_domains else None
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user