Files
ai-trade/services/naver-fetch/naver_client.py

80 lines
3.2 KiB
Python

"""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()