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
Related#
- Collector Incremental Polling Failure Modes: Conditional Requests, Dedupe, and Watermarks
- Collector Pipeline Failure Modes: Recrawl Scheduling, Deduplication, and Zero-Yield Extraction
- Envoy Traffic-Management Failure Modes: Retry Budgets, Timeout Hierarchy, Circuit Breakers, and Outlier Detection Interactions
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와 부합함