improve scraper runtime
This commit is contained in:
@@ -11,6 +11,7 @@ logger = logging.getLogger("iaai_scraper.browser")
|
||||
|
||||
def _build_init_script() -> str:
|
||||
"""JS-патч признаков автоматизации."""
|
||||
# Подмешиваем базовые browser fingerprint overrides.
|
||||
hardware_concurrency = random.choice([4, 8, 12, 16])
|
||||
device_memory = random.choice([4, 8, 16])
|
||||
languages = ["en-US", "en"]
|
||||
@@ -61,11 +62,12 @@ def _build_init_script() -> str:
|
||||
class BrowserFactory:
|
||||
|
||||
def __init__(self, settings: Settings) -> None:
|
||||
# Фабрика браузера и контекстов на основе env-настроек.
|
||||
self.settings = settings
|
||||
|
||||
def create_browser(self, playwright: Playwright) -> Browser:
|
||||
# пробуем Chrome, если нет — Chromium
|
||||
launch_kwargs = {
|
||||
launch_kwargs: dict = {
|
||||
"headless": self.settings.headless,
|
||||
"args": [
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
@@ -74,6 +76,10 @@ class BrowserFactory:
|
||||
"--disable-features=IsolateOrigins,site-per-process",
|
||||
],
|
||||
}
|
||||
proxy_dict = self.settings.proxy.to_playwright_dict()
|
||||
if proxy_dict:
|
||||
launch_kwargs["proxy"] = proxy_dict
|
||||
logger.info("Using proxy: %s", self.settings.proxy.server)
|
||||
try:
|
||||
logger.info("Trying to launch real Chrome channel")
|
||||
return playwright.chromium.launch(channel="chrome", **launch_kwargs)
|
||||
@@ -83,6 +89,7 @@ class BrowserFactory:
|
||||
|
||||
def create_context(self, browser: Browser, storage_state: str | None = None) -> BrowserContext:
|
||||
# рандом viewport/timezone под каждый контекст
|
||||
# Контекст изолирует cookies, headers и fingerprint-параметры.
|
||||
viewport = random.choice(self.settings.fingerprint.viewport_presets)
|
||||
timezone_id = random.choice(self.settings.fingerprint.timezone_candidates)
|
||||
color_scheme = random.choice(["light", "dark"])
|
||||
|
||||
@@ -23,6 +23,7 @@ class NetworkCapture:
|
||||
_origin: str | None = None
|
||||
|
||||
def attach(self, page: Page) -> None:
|
||||
# Подписываемся на request/response события страницы.
|
||||
try:
|
||||
self._origin = urlparse(page.url).netloc.lower() or None
|
||||
except Exception:
|
||||
@@ -31,6 +32,7 @@ class NetworkCapture:
|
||||
page.on("response", self._on_response)
|
||||
|
||||
def _is_same_origin(self, url: str) -> bool:
|
||||
# При необходимости ограничиваемся same-origin и доменами IAAI.
|
||||
if not self.settings.gentle.capture_same_origin_only or not self._origin:
|
||||
return True
|
||||
netloc = urlparse(url).netloc.lower()
|
||||
@@ -45,6 +47,7 @@ class NetworkCapture:
|
||||
if len(self.requests) >= self.settings.gentle.max_requests:
|
||||
return
|
||||
key = f"{request.method}:{request.url}:{request.post_data or ''}"
|
||||
# Дедупликация одинаковых запросов.
|
||||
if key in self._seen_req:
|
||||
return
|
||||
self._seen_req.add(key)
|
||||
@@ -92,6 +95,7 @@ class NetworkCapture:
|
||||
|
||||
@staticmethod
|
||||
def _categorize(url: str) -> str:
|
||||
# Простая эвристика для разбивки ответов по смыслу.
|
||||
low = url.lower()
|
||||
mapping = {
|
||||
"images": ["image", "media", "photos", "gallery"],
|
||||
|
||||
@@ -10,9 +10,11 @@ class HumanPacer:
|
||||
"""Random паузы между действиями."""
|
||||
|
||||
def __init__(self, settings: Settings) -> None:
|
||||
# Инкапсулирует все задержки между действиями браузера.
|
||||
self.settings = settings
|
||||
|
||||
def pause(self, min_s: float, max_s: float) -> None:
|
||||
# Базовая случайная пауза в заданном диапазоне.
|
||||
if self.settings.pace.enabled:
|
||||
time.sleep(random.uniform(min_s, max_s))
|
||||
|
||||
@@ -35,6 +37,7 @@ class HumanPacer:
|
||||
self.pause(self.settings.pace.after_page_change_min_s, self.settings.pace.after_page_change_max_s)
|
||||
|
||||
def move_mouse_to(self, page: Page, locator: Locator) -> None:
|
||||
# Небольшое движение мыши к элементу перед кликом.
|
||||
try:
|
||||
box = locator.bounding_box()
|
||||
except Exception:
|
||||
|
||||
Reference in New Issue
Block a user