Randomise browser fingerprint per context to evade bot detection
Add UA rotation (8 real Chrome UAs), random viewport, en-GB locale, Europe/London timezone, stealth browser args to suppress automation signals, and a per-context init script masking navigator.webdriver, plugins, and chrome runtime. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
83b46bee8f
commit
52455328e7
1 changed files with 47 additions and 7 deletions
|
|
@ -20,6 +20,7 @@ Rate plan variants per room type (typical):
|
|||
|
||||
import asyncio
|
||||
import logging
|
||||
import random
|
||||
import re
|
||||
import uuid
|
||||
from datetime import date
|
||||
|
|
@ -34,6 +35,34 @@ from services import proxy as proxy_util
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_USER_AGENTS = [
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0',
|
||||
]
|
||||
|
||||
_VIEWPORTS = [
|
||||
{'width': 1920, 'height': 1080},
|
||||
{'width': 1366, 'height': 768},
|
||||
{'width': 1440, 'height': 900},
|
||||
{'width': 1536, 'height': 864},
|
||||
{'width': 1280, 'height': 800},
|
||||
{'width': 1600, 'height': 900},
|
||||
]
|
||||
|
||||
# Injected into every new context to mask headless/webdriver signals.
|
||||
_STEALTH_SCRIPT = """
|
||||
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
|
||||
Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
|
||||
Object.defineProperty(navigator, 'languages', {get: () => ['en-GB', 'en']});
|
||||
window.chrome = {runtime: {}};
|
||||
"""
|
||||
|
||||
# JS that extracts all rate plan rows from the room availability table.
|
||||
# Runs inside the page after the room table has loaded.
|
||||
#
|
||||
|
|
@ -179,7 +208,13 @@ class PlaywrightHotelPageBackend(ScraperBackend):
|
|||
if not self._browser:
|
||||
self._browser = await self._pw.chromium.launch(
|
||||
headless=True,
|
||||
args=['--no-sandbox', '--disable-dev-shm-usage'],
|
||||
args=[
|
||||
'--no-sandbox',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-blink-features=AutomationControlled',
|
||||
'--disable-infobars',
|
||||
'--disable-extensions',
|
||||
],
|
||||
)
|
||||
|
||||
async def _get_context(self) -> BrowserContext:
|
||||
|
|
@ -191,16 +226,21 @@ class PlaywrightHotelPageBackend(ScraperBackend):
|
|||
except Exception:
|
||||
pass
|
||||
session_id = uuid.uuid4().hex[:12] if self._proxy_enabled() else None
|
||||
ua = random.choice(_USER_AGENTS)
|
||||
viewport = random.choice(_VIEWPORTS)
|
||||
self._context = await self._browser.new_context(
|
||||
user_agent=(
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
|
||||
'AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
'Chrome/125.0.0.0 Safari/537.36'
|
||||
),
|
||||
user_agent=ua,
|
||||
viewport=viewport,
|
||||
locale='en-GB',
|
||||
timezone_id='Europe/London',
|
||||
**self._proxy_kwargs(session_id),
|
||||
)
|
||||
await self._context.add_init_script(_STEALTH_SCRIPT)
|
||||
self._requests_on_context = 0
|
||||
logger.debug("Opened new browser context" + (f" (proxy session {session_id})" if session_id else ""))
|
||||
logger.debug(
|
||||
f"Opened new browser context ua=…{ua[-30:]} viewport={viewport['width']}x{viewport['height']}"
|
||||
+ (f" proxy_session={session_id}" if session_id else "")
|
||||
)
|
||||
return self._context
|
||||
|
||||
async def _rotate_context(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue