이 장에서 배우는 것 Crawlee의
PuppeteerCrawler에puppeteer-extra-plugin-stealth를 붙이는 법. 그리고 붙이기 전에 확인할 것과, 붙인 뒤에 반드시 겪게 되는 것.
사례 — 콘텐츠 공장의 수집 단계는 terminal 명령 하나입니다.
대부분의 수집은 거기서 끝납니다. 위에서부터 확인하세요.
| 상대가 제공하는 것 | 쓸 것 |
|---|---|
| 공식 API | API |
| RSS · 사이트맵 · JSON 엔드포인트 | 그것 |
| 서버가 이미 렌더한 HTML | fetch + HTML 파서 |
| 브라우저에서 스크립트가 돌아야 나오는 화면 | 그때 비로소 Crawlee |
브라우저는 가장 비싼 선택지입니다. Chrome 한 탭이 수백 MB를 쓰고, 페이지당 시간도 초 단위로 늘어나며, 무엇보다 가장 자주 깨집니다. 상대가 마크업을 조금만 바꿔도 멈춥니다.
graph TD
A["자료가 필요하다"] --> B{"공식 API가 있나"}
B -->|있다| C["API — 여기서 끝"]
B -->|없다| D{"HTML에 이미 들어 있나"}
D -->|있다| E["fetch + 파서"]
D -->|없다| F["브라우저 — Crawlee"]이 확인이 기술보다 먼저입니다. 아래를 어기면 차단이 문제가 아니라 계약과 법률 문제가 됩니다. 스텔스 플러그인은 그 어느 것도 해결해 주지 않습니다.
maxConcurrency와 maxRequestsPerMinute가 그 자리입니다.가장 편한 경우는 내 사이트, 내 데이터, 또는 명시적으로 허가받은 곳입니다. 자기 서비스의 렌더링을 점검하거나 내 상품 정보를 모으는 용도라면 위 대부분이 애초에 문제가 되지 않습니다. 그 밖의 대상이라면 먼저 허락을 구하는 편이 빠릅니다.
설치:
핵심은 launchContext.launcher입니다. Crawlee에게 기본 puppeteer 대신
puppeteer-extra를 써서 브라우저를 띄우라고 알려 주는 자리입니다.
배선은 여기까지입니다. 어려운 것은 이 앞뒤입니다.
이 장에서 가장 중요한 문단입니다.
Crawlee는 자체 브라우저 지문 생성기를 갖고 있고, PuppeteerCrawler와
PlaywrightCrawler에서 기본으로 켜져 있습니다. 예전에 있던 stealth 옵션이
이것으로 대체됐습니다. 즉 아무 설정도 안 한 상태에서 이미 지문이 만들어져
주입되고 있습니다.
여기에 스텔스 플러그인을 얹으면 두 도구가 같은 값을 각자 덮어씁니다.
navigator.webdriver, User-Agent, 언어, 화면 크기 같은 것들입니다. 운이 나쁘면
서로 어긋난 조합이 나와서, 아무것도 안 썼을 때보다 더 눈에 띄는 상태가
됩니다. "스텔스를 붙였는데 오히려 더 막힌다"의 흔한 정체가 이것입니다.
한쪽만 쓰세요.
그 전에 스텔스가 정말 필요한지 확인하세요. Crawlee 기본 지문만으로 되는 경우가 많고, 그러면 의존성이 두 개 줄고 아래의 유지비도 사라집니다. 순서는 "아무것도 안 붙이고 시도 → 안 되면 한쪽만 붙이기"입니다.
| 무엇이 | 어떻게 |
|---|---|
| 스텔스 플러그인 | puppeteer 새 버전을 뒤따라갑니다. 올리자마자 깨지는 조합이 생깁니다 |
| 탐지 쪽 | 계속 바뀝니다. 오늘 되는 것이 다음 달에 안 됩니다 |
| 상대 마크업 | 예고 없이 바뀝니다 |
그래서 성공을 가정한 파이프라인을 만들면 안 됩니다. 조용히 빈 결과를 내려보내는 것이 가장 나쁩니다 — 뒷단계가 그 빈 값으로 계속 돌아갑니다.
예약으로 파이프라인 만들기에서 본 원칙이 여기서도 그대로입니다.
수집 자체는 판단이 없는 작업이라 task가 아니라 terminal이 맞습니다.
정해진 명령이 정해진 형식으로 저장하고 끝납니다.
task에 맡깁니다. 수집과 판단을 한 칸에 섞으면
실패했을 때 어느 쪽이 문제인지 알 수 없습니다.maxConcurrency를 낮게 두고
시작하세요.launchContext.launcher.npm install crawlee puppeteer puppeteer-extra puppeteer-extra-plugin-stealthimport { PuppeteerCrawler } from "crawlee";import puppeteerExtra from "puppeteer-extra";import stealthPlugin from "puppeteer-extra-plugin-stealth";// 크롤러를 만들기 **전에** 등록해야 합니다.puppeteerExtra.use(stealthPlugin());const crawler = new PuppeteerCrawler({ launchContext: { launcher: puppeteerExtra, launchOptions: { headless: true }, }, maxConcurrency: 2, maxRequestsPerMinute: 30, async requestHandler({ page, request, log }) { const title = await page.title(); log.info(`${title} — ${request.url}`); },});await crawler.run(["https://example.com"]);const crawler = new PuppeteerCrawler({ // 스텔스 플러그인을 쓰기로 했다면 Crawlee 지문은 끕니다. browserPoolOptions: { useFingerprints: false }, launchContext: { launcher: puppeteerExtra }, // ...});[완료 조건]- 수집 건수가 0이면 결과 파일을 쓰지 말고 실패로 알릴 것- 평소 건수의 절반 미만이면 경고와 함께 남길 것매일 05:30 · kind: terminalnode collect.mjs