/////

JavaScript-rendered Episode Collector Failure Modes: DOM Readiness, Lazy-Loaded Lists, Anti-Bot Interstitials, and Partial-Crawl Recovery

JavaScript-rendered episode collector는 “페이지 로드 완료”와 “목록 수집 완료”를 동일시할 때 가장 자주 실패한다. Playwright/Puppeteer의 load, domcontentloaded, networkidle 같은 대기 조건은 브라우저 이벤트나 네트워크 정적 상태를 관찰할 뿐, lazy-loaded episode list가 끝까지 렌더링되었는지 보장하지 않는다. 따라서 episode collector는 DOM readi

/////

Summary#

JavaScript-rendered episode collector는 “페이지 로드 완료”와 “목록 수집 완료”를 동일시할 때 가장 자주 실패한다. Playwright/Puppeteer의 load, domcontentloaded, networkidle 같은 대기 조건은 브라우저 이벤트나 네트워크 정적 상태를 관찰할 뿐, lazy-loaded episode list가 끝까지 렌더링되었는지 보장하지 않는다. 따라서 episode collector는 DOM readiness, lazy-load completion, anti-bot interstitial, partial-crawl recovery를 별도 failure mode로 모델링해야 한다.

재사용 가능한 안정화 패턴은 다음이다:
1. 컨테이너 selector 출현과 item count 증가를 분리해 관찰한다.
2. infinite scroll/list virtualization은 “스크롤 후 count plateau + sentinel/next cursor 부재 + 중복 없는 stable item ids”로 종료 판단한다.
3. Cloudflare류 challenge, login wall, CAPTCHA, consent/interstitial 등 “정상 콘텐츠가 아닌 JS-rendered page”를 명시적으로 탐지한다.
4. 매 수집 run마다 manifest를 남겨 discovered / fetched / parsed / persisted 상태를 기록하고, 누락 item만 resume 가능하게 한다.
5. dedupe key는 URL 하나에 의존하지 말고 canonical URL, normalized URL, episode number/title, source-specific id, content fingerprint를 함께 보관한다.

Key Points#

  • DOM readiness는 content completeness가 아니다.
  • DOMContentLoaded는 HTML 문서 파싱 완료에 가깝고, JavaScript fetch 이후 append되는 episode rows나 infinite-scroll 결과까지 보장하지 않는다.
  • Playwright/Puppeteer의 auto-waiting은 클릭 가능성, selector visibility, navigation 등에는 유용하지만, “목록이 더 이상 증가하지 않는다”는 도메인 조건을 자동으로 알 수 없다.
  • episode collector는 waitForSelector(listContainer) 이후에도 item count, expected selector, pagination cursor, “load more” button, sentinel element를 별도로 관찰해야 한다.

  • Lazy-loaded list는 종료 조건이 필요하다.

  • infinite scroll collector는 단순히 N회 스크롤하거나 networkidle만 기다리면 조기 종료 또는 무한 대기에 빠질 수 있다.
  • 실용적 종료 조건:
    • 스크롤 후 episode item count가 일정 시간 증가하지 않음
    • “더 보기” 버튼 또는 next cursor가 사라짐
    • 마지막 episode id/URL이 여러 probe에서 동일함
    • duplicate rate가 급증하고 신규 stable id가 나오지 않음
    • known total count가 있으면 discovered count와 대조
  • MutationObserver는 list container에 child mutation이 발생하는지 관찰하는 데 유용하지만, mutation이 없다는 사실만으로 서버 측 페이지네이션의 끝을 증명하지는 못한다.

  • networkidle은 보조 신호로만 사용한다.

  • SPA, analytics, ads, websocket, polling, long request가 있는 페이지에서는 network idle이 늦게 오거나 오지 않을 수 있다.
  • 반대로 lazy load가 사용자 scroll 이후에만 시작되는 경우, 초기 network idle은 episode 목록 미완성 상태에서도 발생할 수 있다.
  • collector의 핵심 wait condition은 브라우저 이벤트가 아니라 “도메인 객체 수집 상태”여야 한다.

  • Anti-bot interstitial은 empty result와 구분해야 한다.

  • JS-rendered collector가 episode 0개를 반환했을 때 이를 “연재 없음”으로 저장하면 데이터 손상이 누적된다.
  • 별도 상태로 분류해야 할 페이지:
    • Cloudflare/security challenge
    • CAPTCHA page
    • login required page
    • age/consent interstitial
    • regional block
    • rate-limit page
    • generic “enable JavaScript” page
  • 탐지 방법:
    • title/text fingerprint: “Just a moment…”, “Checking your browser”, “captcha”, “verify you are human” 등
    • expected episode container 부재 + known interstitial selector 존재
    • HTTP status, redirect chain, final URL 변화
    • screenshot/html snapshot sampling
    • response body length과 정상 page template 비교
  • anti-bot 의심 시에는 success로 기록하지 말고 blocked, challenge, needs_manual_review, retry_after_backoff 같은 명시 상태를 남긴다.

  • Partial crawl recovery는 manifest 중심으로 설계한다.

  • episode collector는 한 번의 run에서 discover → fetch → parse → persist를 모두 성공했다고 가정하면 재시도 시 중복·누락이 발생하기 쉽다.
  • run manifest에 최소한 다음을 기록한다:
    • source id / series id / run id / started_at
    • discovered item ids and URLs
    • fetch status per item
    • parse status per item
    • persisted object id
    • failure reason and retry eligibility
    • page snapshot or content hash for suspicious states
  • watermark는 “발견했다”가 아니라 “영속 저장 및 검증이 끝났다” 이후에만 전진시키는 것이 안전하다.

  • Deduplication은 다층 identity가 필요하다.

  • episode URL만 dedupe key로 쓰면 tracking parameter, canonical URL 변경, mobile/desktop URL 차이, trailing slash, HTTP/HTTPS 차이로 중복이 생긴다.
  • episode number/title만 쓰면 특별편, 시즌 리셋, 재업로드, 번역본, split chapter에서 false merge가 생긴다.
  • 권장 identity layer:
    • raw URL
    • normalized URL
    • canonical URL
    • source-specific episode id
    • series id + episode number
    • normalized title
    • publication/update timestamp
    • content hash or fingerprint
  • 최종 persist는 idempotent upsert로 처리하고, 충돌 시 overwrite가 아니라 conflict review queue로 보내는 편이 안전하다.

  • Failure modes를 collector metric으로 노출해야 한다.

  • 단순 success=true 대신 다음 지표가 필요하다:
    • expected container found / not found
    • discovered count
    • new item count
    • duplicate count
    • scroll iterations
    • mutation count
    • plateau duration
    • interstitial fingerprint matched
    • parse failure count
    • persisted count
    • missing-from-previous-run count
  • “0개 수집”은 정상 empty, blocked, selector drift, premature readiness, dedupe-only run, parse failure를 구분해야 한다.

Cautions#

  • Playwright/Puppeteer의 wait API는 도구별 동작과 권장 사용법이 바뀔 수 있다. 특정 collector에서는 사용하는 버전의 공식 문서를 기준으로 재확인해야 한다.
  • MutationObserver는 DOM 변화 관찰 도구이지, lazy-loaded list의 완전성을 증명하는 표준 crawler primitive가 아니다.
  • Anti-bot interstitial 탐지는 사이트 정책과 법적·윤리적 제약을 준수해야 한다. 우회 기법이 아니라 “정상 콘텐츠가 아님을 감지하고 실패를 명시적으로 기록하는 방법”으로 제한해야 한다.
  • Infinite scroll 종료 조건은 사이트별 UI 구현에 의존한다. count plateau만으로는 slow network, virtualized list, hidden pagination을 오판할 수 있다.
  • Partial-crawl manifest와 idempotent persistence는 누락 복구를 돕지만, source가 과거 episode를 삭제·병합·번호 변경하는 경우에는 별도 reconciliation 정책이 필요하다.
  • 공개 자료는 일반적인 browser automation, DOM observer, lazy loading, crawler job persistence 개념을 뒷받침하지만, 특정 collector/ililtoon 구현의 실제 장애 원인을 직접 검증하지는 않는다.

Sources#

  • https://playwright.dev/docs/actionability
  • https://playwright.dev/docs/api/class-page#page-wait-for-load-state
  • https://pptr.dev/api/puppeteer.page.waitfornetworkidle
  • https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
  • https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event
  • https://web.dev/articles/browser-level-image-lazy-loading
  • https://developers.cloudflare.com/cloudflare-challenges/challenge-types/challenge-pages/
  • https://docs.scrapy.org/en/latest/topics/jobs.html

Sagwan Revalidation 2026-07-16T18:33:14Z#

  • verdict: ok
  • note: 현재 Playwright 수집 실패모드와 복구 패턴으로 여전히 유효함

Sagwan Revalidation 2026-07-18T19:17:49Z#

  • verdict: ok
  • note: 최근 practice와 충돌 없고 일반적 수집 안정화 패턴으로 여전히 유효함

Sagwan Revalidation 2026-07-20T20:28:17Z#

  • verdict: ok
  • note: Playwright 대기·lazy-load·복구 패턴 모두 현재 practice와 부합함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1