Extract shared proxy module; make it available to direct scraper; remove dead code

Proxy config, DataImpulse sticky-session username syntax, and Playwright/
httpx proxy builders now live in one place (services/proxy.py) instead of
being duplicated across the Booking.com backend and the /config/proxy test
endpoint. Both scrapers consume it.

- services/proxy.py: load_config/normalize (DB-authoritative, env fallback),
  new_session_id, username, playwright_proxy, httpx_proxy_url
- PlaywrightLocalBackend delegates proxy building to the module
- get_scraper_backend factory uses proxy.load_config (one resolution path)
- test_proxy_config endpoint uses the shared URL builder; httpx proxies= ->
  proxy= (forward-compatible, 0.28-safe)
- Direct booking-engine scraper (httpx) can now route through the same proxy,
  gated by the direct_scraper_use_proxy flag (default off, plumbing ready)

Dead code removed: set_scraper_paused (never called — rotate-on-block
replaced pause-on-block), get_competitor_matrix / get_hotels_list /
update_hotel_tier (endpoints have their own SQL), unused PROXY_KEYS tuple.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-05 22:55:24 +00:00
parent 6b7f00b40a
commit 1dc8c0a945
5 changed files with 147 additions and 194 deletions

View file

@ -11,6 +11,7 @@ import httpx
from sqlalchemy import text
from database import SyncSessionLocal
from services import proxy as proxy_util
from services.direct_profiles import get_profile
log = logging.getLogger(__name__)
@ -18,6 +19,17 @@ log = logging.getLogger(__name__)
REQUEST_DELAY = 10.0
DISCOVERY_DELAY = 2.0
def _direct_proxy_url() -> str | None:
"""Proxy URL for direct-engine scraping, or None. Shares the Booking.com
proxy config but is gated by the `direct_scraper_use_proxy` flag (default
off booking-engine APIs usually don't need it). Ready to switch on."""
db = SyncSessionLocal()
try:
return proxy_util.direct_httpx_proxy(db)
finally:
db.close()
# Track running discovery scrapes: hotel_id -> status dict
_discovery_status: dict[int, dict] = {}
@ -50,7 +62,7 @@ async def run_discovery(hotel_id: int, profile_name: str, params: dict):
found_rooms: set[str] = set()
found_rates: set[str] = set()
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(proxy=_direct_proxy_url()) as client:
for i, arrival in enumerate(dates):
await asyncio.sleep(DISCOVERY_DELAY)
try:
@ -127,7 +139,7 @@ def run_scrape(hotel_id: int, profile_name: str, params: dict):
async def _run_scrape_async(hotel_id: int, profile, params: dict, scraped_at: datetime):
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(proxy=_direct_proxy_url()) as client:
try:
arrival_dates = await profile.fetch_arrival_dates(client, params)
except Exception as e: