Skip to content

크롤러 유지보수

작성: 0chil

카페24 크롤러 유지보수 플레이북

Section titled “카페24 크롤러 유지보수 플레이북”

Relizm, Vinkg, VintageOk 3건 재작성에서 사용한 실전 절차/규칙/셀렉터를 정리한다.

  • 레거시 Crawler + crawlFrom 구조를 CrawlerV3 + crawl 구조로 재작성한다.
  • 스크립트 문자열 파싱 없이 DOM 기반으로 상태/가격/상세를 안정적으로 파싱한다.
  • 테스트는 shouldContain 위주가 아니라 가능한 항목을 정확 일치(shouldBe)로 검증한다.
  1. 구현 레벨
  • 새/수정 크롤러는 CrawlerV3 구현을 기본으로 한다.
  • Crawled 필드(status, name, sourcedPrice, brand, url, source, identifierInSource, thumbnailUrl, detailImages, detail)를 채운다.
  • source"CAFE24"로 통일한다.
  • Crawled에 없는 값(예: 사이즈, 별도 실측 모델)을 위해 추가 파서/헬퍼/중간 모델을 만들지 않는다.
  • 구현은 결과적으로 Crawled를 채우는 데 필요한 범위까지만 유지한다.
  1. 상태 판별
  • 우선순위 1: 품절 아이콘 (img[alt=품절]) 여부
  • 우선순위 2: 구매 버튼(product_submit)의 visible/hidden 상태
  • 우선순위 3: Sold Out 배지 visible 여부
  • 스크립트 변수(is_soldout_icon) 파싱은 최후 수단이며 기본적으로 사용하지 않는다.
  1. 가격 파싱
  • 우선순위 1: meta[property='product:sale_price:amount']
  • 우선순위 2: 화면 가격 노드(#span_product_price_sale, 판매가 행 등)
  • 실패 시 originalPrice로 fallback
  • 숫자 추출은 공통적으로 filter(Char::isDigit) 사용
  1. URL 보정
  • 이미지 URL은 항상 CompleteUrl.with(domain, raw)로 정규화한다.
  • ec-data-src, data-src, src 순으로 fallback 처리한다.
  • 중복 이미지는 distinct() 처리한다.
  1. 상세(detail) 파싱
  • detail을 위해 실측 라인 판별, 사이즈 해석 같은 추가 의미 해석 로직을 먼저 만들지 않는다.
  • 전체 텍스트가 너무 불안정하거나 운영상 명확한 필요가 있을 때만 예외적으로 detail 추출 규칙을 추가한다.
  1. 테스트 작성
  • 판매중/품절 URL 각각 1개 이상을 실 URL로 고정한다.
  • status, name, brand, sourcedPrice, thumbnailUrl, detailImages, detail을 검증한다.
  • detailImages는 전체 리스트 정확 일치(shouldBe listOf(...))로 검증한다.
  • detail도 가능한 한 정확 문자열 일치로 검증한다.
  1. HTML 수집
Terminal window
curl -sL '<판매중 URL>' > /tmp/<shop>_sale.html
curl -sL '<품절 URL>' > /tmp/<shop>_oos.html
  1. 핵심 셀렉터 확인
Terminal window
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.html
grep -n "..." /tmp/<shop>_oos.html
  1. 구현 재작성
  • supports/product/ URL 기준으로 넓게 잡는다.
  • 상태 파싱은 DOM visible 기준으로 작성한다.
  • 가격/썸네일/상세이미지/상세는 fallback 체인을 넣는다.
  • Crawled에 직접 쓰이지 않는 보조 개념/헬퍼는 만들지 않는다.
  1. 테스트 재작성
  • 과거 URL/레거시 필드(crawlFrom, CrawledProduct) 의존 제거
  • 현재 실 URL 2건 기준으로 고정
  1. 검증
Terminal window
./gradlew compileKotlin compileTestKotlin
  • crawlerTest는 필터가 걸려 있으면 init script로 특정 테스트만 실행한다.
Terminal window
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-tasks
  • 상태
    • .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에서 텍스트 추출
  • 상태
    • .headingArea .icon img[alt=품절]
    • .xans-product-action .ec-base-button.gColumnproduct_submit 버튼 visibility
    • .btnEmSOLD OUT 텍스트
  • 상품명/브랜드
    • .xans-product-detaildesign상품명
  • 상세
    • 사이즈 행 텍스트를 detail로 사용
  • 상세 이미지
    • #prdDetail img에서 detail_footer 제외
  • 상태
    • .headingArea .icon img[alt=품절]
    • .xans-product-action .nonvproduct_submit 버튼 visibility
    • .nonv > spanSold Out
  • 상품명
    • .product_name_css > span 마지막 값
  • 브랜드
    • .custom_option3_css > span 마지막 값
    • fallback: #prdDetail 텍스트에서 브랜드 : ... 정규식
  • 상세 이미지
    • #prdDetail img 전체 (정규화 + 중복 제거)
  • detail
    • 실측사이즈 단면 (cm) 이후 ~ 사이즈 : 이전 구간 추출
  • 판매중/품절 실 URL 각 1개 확보
  • HTML snapshot 저장(/tmp)
  • 상태 셀렉터를 스크립트 없이 DOM에서 확정
  • 가격 fallback 체인 확인
  • detailImages 정규화/중복제거 확인
  • detail 추출 규칙(정확 문자열) 확인
  • 테스트 2건(판매중/품절) 정확 일치 작성
  • compileKotlin, compileTestKotlin, 대상 crawlerTest 통과 확인
  • displaynone 클래스만 보고 판단하면 inline style(display:none) 케이스를 놓칠 수 있다.
  • og:title이 브랜드/사이즈 포함 문자열일 수 있어 가공 규칙이 필요하다.
  • 일부 스토어는 품절이어도 메타 가격이 유지되므로, 가격과 상태를 분리해서 판단해야 한다.
  • detail을 위해 불필요한 의미 해석 helper를 추가하면 유지보수 포인트만 늘고 실제 Crawled 품질에는 도움이 안 되는 경우가 많다.
  • 테스트에서 shouldContain만 쓰면 페이지 구조 변경을 빨리 감지하지 못한다.