"""Naver land client — Playwright 토큰 획득 + httpx article fetch.""" from __future__ import annotations import logging import httpx logger = logging.getLogger(__name__) _UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36") _API = "https://new.land.naver.com/api/articles" class NaverClient: def __init__(self, real_estate_type: str, token_url: str, headless: bool = True, timeout: float = 20.0): self._ret = real_estate_type self._token_url = token_url self._headless = headless self._client = httpx.AsyncClient(timeout=timeout) self._token: str | None = None self._cookies: str = "" async def close(self) -> None: await self._client.aclose() async def acquire_token(self) -> None: """new.land.naver.com 로드 → XHR Authorization 헤더 인터셉트.""" from playwright.async_api import async_playwright captured: dict[str, str] = {} async with async_playwright() as p: browser = await p.chromium.launch(headless=self._headless) try: ctx = await browser.new_context(user_agent=_UA, locale="ko-KR") page = await ctx.new_page() def on_request(req): auth = req.headers.get("authorization") if auth and "bearer" in auth.lower() and "auth" not in captured: captured["auth"] = auth page.on("request", on_request) try: await page.goto(self._token_url, wait_until="networkidle", timeout=45000) except Exception: logger.warning("naver token goto 지연/부분 실패 — 캡처 계속") await page.wait_for_timeout(4000) cookies = await ctx.cookies() self._cookies = "; ".join(f"{c['name']}={c['value']}" for c in cookies) finally: await browser.close() token = captured.get("auth") if not token: raise RuntimeError("naver Bearer token 획득 실패") self._token = token logger.info("naver token 획득 (len=%d)", len(token)) async def fetch_articles(self, cortar_no: str, page: int = 1) -> dict: if not self._token: raise RuntimeError("token 없음 — acquire_token 먼저 호출") params = { "cortarNo": cortar_no, "order": "rank", "realEstateType": self._ret, "tradeType": "", "rentPriceMin": 0, "rentPriceMax": 900000000, "priceMin": 0, "priceMax": 900000000, "areaMin": 0, "areaMax": 900000000, "showArticle": "false", "sameAddressGroup": "false", "priceType": "RETAIL", "page": page, } headers = { "authorization": self._token, "user-agent": _UA, "referer": "https://new.land.naver.com/", "accept": "application/json, text/plain, */*", "accept-language": "ko-KR,ko;q=0.9", "cookie": self._cookies, } resp = await self._client.get(_API, params=params, headers=headers) resp.raise_for_status() return resp.json()