본문으로 건너뛰기

Puppeteer Stealth 플러그인과 playwright-extra 사용하기

puppeteer-extraplaywright-extra는 커뮤니티에서 개발한 라이브러리로, puppeteerplaywright에 플러그인 시스템을 추가하여 기능을 확장할 수 있게 해줍니다. 예를 들어, Puppeteer Stealth 플러그인(puppeteer-extra-plugin-stealth)을 사용하면 봇 탐지를 우회하는 데 도움이 됩니다.

사용 가능한 플러그인

puppeteer-extra 플러그인 목록에서 사용 가능한 플러그인들을 확인할 수 있습니다.

playwright의 경우, playwright-extra 플러그인 목록을 참고해 주세요.

이 예제에서는 Puppeteer Stealth(puppeteer-extra-plugin-stealth) 플러그인을 사용하여 웹 크롤링 시 봇 탐지를 피하는 방법을 설명합니다.

시작하기 전에

선호하는 패키지 매니저를 통해 Puppeteer Extra(puppeteer-extra)와 Puppeteer Stealth 플러그인(puppeteer-extra-plugin-stealth)을 설치해야 합니다.

npm install puppeteer-extra puppeteer-extra-plugin-stealth

Apify 플랫폼에서 이 예제를 실행하려면 Dockerfile에서 apify/actor-node-puppeteer-chrome 이미지를 선택하세요.

Run on
src/crawler.ts
import { PuppeteerCrawler } from 'crawlee';
import puppeteerExtra from 'puppeteer-extra';
import stealthPlugin from 'puppeteer-extra-plugin-stealth';

// puppeteer-extra에 원하는 플러그인을 추가합니다.
// 일부 플러그인은 설정 옵션이 있을 수 있으니 문서를 참고하세요!
puppeteerExtra.use(stealthPlugin());

// PuppeteerCrawler 클래스의 인스턴스를 생성합니다.
// 이는 헤드리스 Chrome/Puppeteer에서 URL을 자동으로 로드하는 크롤러입니다.
const crawler = new PuppeteerCrawler({
launchContext: {
// !!! Crawlee가 puppeteer-extra를 실행기로 사용하도록 이 옵션을 지정해야 합니다 !!!
launcher: puppeteerExtra,
launchOptions: {
// 다른 puppeteer 옵션들도 평소처럼 사용 가능합니다
headless: true,
},
},

// 크롤링할 페이지 수 제한
maxRequestsPerCrawl: 50,

// 각 URL을 크롤링할 때마다 이 함수가 호출됩니다.
// 여기서 익숙한 Puppeteer 스크립트를 작성할 수 있으며,
// 브라우저와 페이지는 Crawlee가 자동으로 관리합니다.
// 이 함수는 다음 필드를 포함하는 객체를 매개변수로 받습니다:
// - request: URL 및 HTTP 메서드와 같은 정보가 있는 Request 클래스의 인스턴스
// - page: Puppeteer의 Page 객체 (참고: https://pptr.dev/#show=api-class-page)
async requestHandler({ pushData, request, page, enqueueLinks, log }) {
log.info(`${request.url} 처리 중...`);

// 브라우저 컨텍스트 내에서 Puppeteer가 실행할 함수입니다.
const data = await page.$$eval('.athing', ($posts) => {
const scrapedData: { title: string; rank: string; href: string }[] = [];

// Hacker News의 각 게시물의 제목, 순위, URL을 가져옵니다.
$posts.forEach(($post) => {
scrapedData.push({
title: $post.querySelector('.title a').innerText,
rank: $post.querySelector('.rank').innerText,
href: $post.querySelector('.title a').href,
});
});

return scrapedData;
});

// 결과를 기본 데이터셋에 저장합니다.
await pushData(data);

// 다음 페이지 링크를 찾아 있다면 큐에 추가합니다.
const infos = await enqueueLinks({
selector: '.morelink',
});

if (infos.processedRequests.length === 0) log.info(`${request.url}이(가) 마지막 페이지입니다!`);
},

// 페이지 처리가 maxRequestRetries+1회 이상 실패하면 이 함수가 호출됩니다.
failedRequestHandler({ request, log }) {
log.error(`${request.url} 요청이 너무 많이 실패했습니다.`);
},
});

await crawler.addRequests(['https://news.ycombinator.com/']);

// 크롤러를 실행하고 완료될 때까지 대기합니다.
await crawler.run();

console.log('크롤러가 완료되었습니다.');