크롤러 유지보수
작성: 0chil카페24 크롤러 유지보수 플레이북
Section titled “카페24 크롤러 유지보수 플레이북”Relizm, Vinkg, VintageOk 3건 재작성에서 사용한 실전 절차/규칙/셀렉터를 정리한다.
- 레거시
Crawler+crawlFrom구조를CrawlerV3+crawl구조로 재작성한다. - 스크립트 문자열 파싱 없이 DOM 기반으로 상태/가격/상세를 안정적으로 파싱한다.
- 테스트는
shouldContain위주가 아니라 가능한 항목을 정확 일치(shouldBe)로 검증한다.
2. 공통 원칙
Section titled “2. 공통 원칙”- 구현 레벨
- 새/수정 크롤러는
CrawlerV3구현을 기본으로 한다. Crawled필드(status,name,sourcedPrice,brand,url,source,identifierInSource,thumbnailUrl,detailImages,detail)를 채운다.source는"CAFE24"로 통일한다.Crawled에 없는 값(예: 사이즈, 별도 실측 모델)을 위해 추가 파서/헬퍼/중간 모델을 만들지 않는다.- 구현은 결과적으로
Crawled를 채우는 데 필요한 범위까지만 유지한다.
- 상태 판별
- 우선순위 1: 품절 아이콘 (
img[alt=품절]) 여부 - 우선순위 2: 구매 버튼(
product_submit)의 visible/hidden 상태 - 우선순위 3:
Sold Out배지 visible 여부 - 스크립트 변수(
is_soldout_icon) 파싱은 최후 수단이며 기본적으로 사용하지 않는다.
- 가격 파싱
- 우선순위 1:
meta[property='product:sale_price:amount'] - 우선순위 2: 화면 가격 노드(
#span_product_price_sale, 판매가 행 등) - 실패 시
originalPrice로 fallback - 숫자 추출은 공통적으로
filter(Char::isDigit)사용
- URL 보정
- 이미지 URL은 항상
CompleteUrl.with(domain, raw)로 정규화한다. ec-data-src,data-src,src순으로 fallback 처리한다.- 중복 이미지는
distinct()처리한다.
- 상세(detail) 파싱
detail을 위해 실측 라인 판별, 사이즈 해석 같은 추가 의미 해석 로직을 먼저 만들지 않는다.- 전체 텍스트가 너무 불안정하거나 운영상 명확한 필요가 있을 때만 예외적으로 detail 추출 규칙을 추가한다.
- 테스트 작성
- 판매중/품절 URL 각각 1개 이상을 실 URL로 고정한다.
status,name,brand,sourcedPrice,thumbnailUrl,detailImages,detail을 검증한다.detailImages는 전체 리스트 정확 일치(shouldBe listOf(...))로 검증한다.detail도 가능한 한 정확 문자열 일치로 검증한다.
3. 작업 절차 (반복 템플릿)
Section titled “3. 작업 절차 (반복 템플릿)”- HTML 수집
curl -sL '<판매중 URL>' > /tmp/<shop>_sale.htmlcurl -sL '<품절 URL>' > /tmp/<shop>_oos.html- 핵심 셀렉터 확인
grep -n "og:title\|og:image\|product:price:amount\|product:sale_price:amount\|품절\|Sold out\|xans-product-action\|prdDetail\|ec-data-src" /tmp/<shop>_sale.htmlgrep -n "..." /tmp/<shop>_oos.html- 구현 재작성
supports를/product/URL 기준으로 넓게 잡는다.- 상태 파싱은 DOM visible 기준으로 작성한다.
- 가격/썸네일/상세이미지/상세는 fallback 체인을 넣는다.
Crawled에 직접 쓰이지 않는 보조 개념/헬퍼는 만들지 않는다.
- 테스트 재작성
- 과거 URL/레거시 필드(
crawlFrom,CrawledProduct) 의존 제거 - 현재 실 URL 2건 기준으로 고정
- 검증
./gradlew compileKotlin compileTestKotlincrawlerTest는 필터가 걸려 있으면 init script로 특정 테스트만 실행한다.
cat > /tmp/<shop>-crawler-test.init.gradle <<'EOF'allprojects { tasks.withType(Test).configureEach { useJUnitPlatform() if (name == 'crawlerTest') { filter { setFailOnNoMatchingTests(true) includeTestsMatching 'dev.vingle.product.crawling.crawler.<CrawlerTestClass>' includeTestsMatching 'dev.vingle.product.crawling.crawler.<CrawlerTestClass>.*' } } }}EOF./gradlew crawlerTest -I /tmp/<shop>-crawler-test.init.gradle --rerun-tasks4. 도메인별 실제 포인트
Section titled “4. 도메인별 실제 포인트”Relizm (relizm.com)
Section titled “Relizm (relizm.com)”- 상태
.xans-product-action .btnArea a[onclick*=product_submit]visible이면SALE- 버튼 hidden +
Sold out배지 visible이면OUT_OF_STOCK
- 제목 파싱
[BRAND]상품명[SIZE : ...]패턴- source:
og:title,.xans-product-action tbody td,.xans-product table td > span
- 상세 이미지
.xans-product-additional #prdDetail img, #detailArea img
- detail
#prdDetailContent/#prdDetail/#detailArea에서 텍스트 추출
Vinkg (vinkg.com)
Section titled “Vinkg (vinkg.com)”- 상태
.headingArea .icon img[alt=품절].xans-product-action .ec-base-button.gColumn내product_submit버튼 visibility.btnEm의SOLD OUT텍스트
- 상품명/브랜드
.xans-product-detaildesign의상품명행
- 상세
사이즈행 텍스트를 detail로 사용
- 상세 이미지
#prdDetail img에서detail_footer제외
VintageOk (vintageok.co.kr)
Section titled “VintageOk (vintageok.co.kr)”- 상태
.headingArea .icon img[alt=품절].xans-product-action .nonv의product_submit버튼 visibility.nonv > span의Sold Out
- 상품명
.product_name_css > span마지막 값
- 브랜드
.custom_option3_css > span마지막 값- fallback:
#prdDetail텍스트에서브랜드 : ...정규식
- 상세 이미지
#prdDetail img전체 (정규화 + 중복 제거)
- detail
실측사이즈 단면 (cm)이후 ~사이즈 :이전 구간 추출
5. 유지보수 체크리스트
Section titled “5. 유지보수 체크리스트”- 판매중/품절 실 URL 각 1개 확보
- HTML snapshot 저장(
/tmp) - 상태 셀렉터를 스크립트 없이 DOM에서 확정
- 가격 fallback 체인 확인
- detailImages 정규화/중복제거 확인
- detail 추출 규칙(정확 문자열) 확인
- 테스트 2건(판매중/품절) 정확 일치 작성
-
compileKotlin,compileTestKotlin, 대상 crawlerTest 통과 확인
6. 자주 발생한 이슈
Section titled “6. 자주 발생한 이슈”displaynone클래스만 보고 판단하면 inline style(display:none) 케이스를 놓칠 수 있다.og:title이 브랜드/사이즈 포함 문자열일 수 있어 가공 규칙이 필요하다.- 일부 스토어는 품절이어도 메타 가격이 유지되므로, 가격과 상태를 분리해서 판단해야 한다.
detail을 위해 불필요한 의미 해석 helper를 추가하면 유지보수 포인트만 늘고 실제Crawled품질에는 도움이 안 되는 경우가 많다.- 테스트에서
shouldContain만 쓰면 페이지 구조 변경을 빨리 감지하지 못한다.