Skip to content

Commit 8cdde41

Browse files
author
erkanrzgc
committed
update
1 parent 463063f commit 8cdde41

2 files changed

Lines changed: 32 additions & 15 deletions

File tree

modules/stealth/playwright_fallback.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
here and get back the DOM after ``domcontentloaded`` (or a custom
77
selector) has fired.
88
9-
Playwright is an **optional** dependency. The module imports cleanly
10-
without it and exposes ``AVAILABLE = False``.
9+
Browser priority:
10+
1. Patchright (stealth Playwright fork, 30/30 bot-detection tests)
11+
2. Playwright (standard, requires manual stealth patches)
12+
3. None (aiohttp-only fallback)
13+
14+
All are **optional** dependencies. The module imports cleanly without
15+
any of them and exposes ``AVAILABLE = False``.
1116
"""
1217

1318
from __future__ import annotations
@@ -20,15 +25,27 @@
2025

2126
log = logging.getLogger(__name__)
2227

23-
try:
24-
from playwright.async_api import ( # type: ignore[import-not-found]
25-
async_playwright,
26-
)
28+
# ── browser backend selection ──────────────────────────────────────────
29+
_async_playwright = None
30+
_BACKEND = ""
2731

32+
try:
33+
from patchright.async_api import async_playwright as _patchright_playwright # type: ignore[import-not-found]
34+
_async_playwright = _patchright_playwright
35+
_BACKEND = "patchright"
2836
AVAILABLE = True
29-
except ImportError: # pragma: no cover - optional dep
30-
async_playwright = None # type: ignore[assignment]
31-
AVAILABLE = False
37+
except ImportError:
38+
try:
39+
from playwright.async_api import ( # type: ignore[import-not-found]
40+
async_playwright as _pw,
41+
)
42+
_async_playwright = _pw
43+
_BACKEND = "playwright"
44+
AVAILABLE = True
45+
except ImportError: # pragma: no cover - optional dep
46+
AVAILABLE = False
47+
48+
log.debug("browser backend: %s (available=%s)", _BACKEND or "none", AVAILABLE)
3249

3350

3451
@dataclass(frozen=True)
@@ -57,19 +74,19 @@ async def fetch_rendered(
5774
screenshot_dir: Path | None = None,
5875
screenshot_name: str | None = None,
5976
) -> RenderedPage | None:
60-
"""Fetch ``url`` via a headless Chromium.
77+
"""Fetch ``url`` via a headless Chromium (Patchright or Playwright).
6178
62-
Returns None if Playwright is missing or the render failed. Callers
79+
Returns None if no browser is installed or the render failed. Callers
6380
should treat None as "fallback unavailable, move on". When
6481
``screenshot_dir`` is provided a PNG is saved inside it and the path
6582
is returned on ``RenderedPage.screenshot_path``.
6683
"""
6784
if not AVAILABLE:
68-
log.debug("playwright not installed; skipping rendered fetch for %s", url)
85+
log.debug("no browser backend installed; skipping rendered fetch for %s", url)
6986
return None
7087

7188
try:
72-
async with async_playwright() as pw: # type: ignore[misc]
89+
async with _async_playwright() as pw: # type: ignore[misc]
7390
launch_args: dict[str, object] = {"headless": True}
7491
if proxy:
7592
launch_args["proxy"] = {"server": proxy}
@@ -112,5 +129,5 @@ async def fetch_rendered(
112129
except asyncio.CancelledError:
113130
raise
114131
except Exception as exc: # noqa: BLE001 - fallback must not crash scan
115-
log.debug("playwright render failed for %s: %s", url, exc)
132+
log.debug("browser render failed for %s: %s", url, exc)
116133
return None

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ ghunt = ["ghunt>=2.3.0", "httpx>=0.24.0"]
4242
toutatis = ["toutatis>=1.4"]
4343
phone = ["phonenumbers>=8.13"]
4444
tor = ["stem>=1.8.2"]
45-
browser = ["playwright>=1.40.0", "trafilatura>=1.12.0"]
45+
browser = ["playwright>=1.40.0", "patchright>=1.45.0", "trafilatura>=1.12.0"]
4646
filetype = ["magika>=0.6.0"]
4747
ai = [
4848
"llama-cpp-python>=0.3.0",

0 commit comments

Comments
 (0)