o2o-negosium-original/lps/services/search/esm/selectors.py
민헌 e365e5c6e0 feat(lps): G마켓·옥션·11번가 크롤 어댑터 + 브라우저 어댑터 베이스
네이버 폴백용 오픈마켓 크롤러 3종. 4중 복제를 피해 BrowserSearchAdapter 베이스로
브라우저 수명·프록시 회전·차단감지+IP회전 재시도·감지기록을 공유하고, 사이트 차이는
훅(_search_url/_parse/ready_selector/block_markers)으로 분리.

- browser_base: patchright 공통 machinery(쿠팡 machinery 일반화)
- card_parser: 오픈마켓 공용 카드 파서(CardConfig 주입) + util(extract_price/clean_name/shipping)
- esm: G마켓·옥션(eBay 백엔드 공유, site 파라미터 1개로 커버, '잠시만' 챌린지 대기)
- st11: 11번가 PC(공식API 판매자계정 필요라 불가). 지연로딩 → scroll_steps, '.c-card-item' 토큰
- 학습 반영: 오픈마켓은 리소스차단 OFF(렌더/챌린지 깨짐), 스턱 챌린지='잠시만' 잔존 시 차단 간주→IP회전
- 파서 결정론 테스트 7종(실렌더 카드 3개씩 경량 fixture). 라이브 검증: 신선 IP로 3사 모두 파싱 성공

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:41:05 +09:00

39 lines
1.3 KiB
Python

"""ESM(G마켓·옥션, eBay Korea 공유 백엔드) 검색결과 셀렉터/설정.
두 사이트는 백엔드를 공유하지만 검색결과 HTML 템플릿은 서로 달라, 카드/이름/가격 셀렉터가
다르다(일부 가격 클래스만 공유). 사이트별 설정을 외부화한다 — 레이아웃 변경 시 여기만.
"""
from dataclasses import dataclass
from services.search.card_parser import CardConfig
@dataclass(frozen=True)
class EsmSite:
search_url: str # {q} 자리표시자
ready_selector: str # 챌린지 통과·렌더 완료 신호
cards: CardConfig
GMARKET = EsmSite(
search_url="https://browse.gmarket.co.kr/search?keyword={q}",
ready_selector="div.box__item-container",
cards=CardConfig(
source="gmarket", mall_name="G마켓", base_url="https://browse.gmarket.co.kr/",
card="div.box__item-container", name="[class*=text__item-title]", price="[class*=box__price-seller]",
),
)
AUCTION = EsmSite(
search_url="https://browse.auction.co.kr/search?keyword={q}",
ready_selector="div.itemcard",
cards=CardConfig(
source="auction", mall_name="옥션", base_url="https://browse.auction.co.kr/",
card="div.itemcard", name="span.text--title", price="strong.text__price-seller",
link="a.link--itemcard",
),
)
SITES = {"gmarket": GMARKET, "auction": AUCTION}