본문으로 건너뛰기

첫 번째 크롤러

이제 첫 번째 크롤러를 만들어보겠습니다. 그전에 Crawlee의 주요 클래스들에 대해 간단히 알아보겠습니다.

Crawlee의 작동 방식

Crawlee에서는 3가지 주요 크롤러 클래스를 사용할 수 있습니다.

이들의 차이점은 나중에 설명하고, 지금은 공통점에 대해 이야기해보겠습니다.

모든 크롤러의 기본 동작 방식은 웹 페이지에 접속하여 열고, 필요한 작업을 수행하고, 결과를 저장한 다음, 다음 페이지로 이동하여 이 과정을 반복하는 것입니다. 크롤러가 작업을 수행하기 위해서는 두 가지 질문에 대한 답이 필요합니다: 어디로 가야 하는가? 그리고 거기서 무엇을 해야 하는가? 이 두 가지만 설정하면 됩니다. 나머지는 크롤러가 적절한 기본값으로 처리합니다.

'어디로' - RequestRequestQueue

모든 크롤러는 Request 클래스의 인스턴스를 사용하여 방문할 페이지를 결정합니다. 각 요청에는 많은 정보가 포함될 수 있지만, 최소한 URL - 방문할 웹 페이지 주소는 반드시 포함되어야 합니다. 하지만 단 하나의 URL만으로는 크롤링이 의미가 없겠죠. 때로는 미리 준비된 수천 개의 URL 목록이 있을 수 있고, 다른 경우에는 크롤링하면서 동적으로 URL을 추가해야 할 수도 있습니다. 대부분의 경우 두 방식을 모두 사용하게 됩니다.

이러한 요청들은 RequestQueue에 저장됩니다. 이는 Request 인스턴스들의 동적 큐입니다. 시작 URL을 미리 설정할 수도 있고, 크롤러가 실행되는 동안 더 많은 요청을 추가할 수도 있습니다. 이를 통해 크롤러는 한 페이지를 열고, 관심 있는 URL(예: 같은 도메인의 다른 페이지 링크)을 추출하여 큐에 추가(인큐)하고, 이 과정을 반복하여 사실상 무제한의 URL을 처리할 수 있습니다.

'무엇을' - requestHandler

requestHandler에서는 크롤러가 방문하는 각 페이지에서 수행할 작업을 정의합니다. 페이지에서 데이터를 추출하거나, 데이터를 처리하고, 저장하거나, API를 호출하거나, 계산을 수행하는 등의 작업을 할 수 있습니다.

requestHandler는 사용자가 정의하는 함수로, 크롤러가 RequestQueue의 각 Request에 대해 자동으로 호출합니다. 이 함수는 항상 하나의 인자 - CrawlingContext를 받습니다. 사용하는 크롤러 클래스에 따라 속성이 달라지지만, 항상 현재 크롤링 중인 URL과 관련 메타데이터를 나타내는 request 속성을 포함합니다.

크롤러 만들기

이론을 실제로 적용해보면서 간단한 것부터 시작해보겠습니다. 페이지를 방문하여 HTML 제목을 가져오는 것입니다. 이 튜토리얼에서는 Crawlee 웹사이트 https://crawlee.dev를 스크래핑하지만, 같은 코드로 다른 웹사이트도 크롤링할 수 있습니다.

Top level await 설정

예제에서는 Top level await라는 JavaScript 기능을 사용합니다. 이를 사용하려면 추가 설정이 필요할 수 있습니다. ECMAScript Modules을 사용해야 하므로, package.json"type": "module"을 추가하거나 파일 확장자를 *.mjs로 사용해야 합니다. TypeScript 프로젝트의 경우, moduletarget 컴파일러 옵션을 ES2022 이상으로 설정해야 합니다.

크롤링 큐에 요청 추가하기

앞서 크롤러가 URL을 크롤링하기 위해 요청 큐를 사용한다고 배웠습니다. 이제 큐를 만들고 첫 번째 요청을 추가해보겠습니다.

src/main.js
import { RequestQueue } from 'crawlee';

// 먼저 요청 큐 인스턴스를 생성합니다.
const requestQueue = await RequestQueue.open();
// 그리고 하나 이상의 요청을 추가합니다.
await requestQueue.addRequest({ url: 'https://crawlee.dev' });

requestQueue.addRequest() 함수는 URL 문자열이 포함된 객체를 자동으로 Request 인스턴스로 변환합니다. 이제 requestQueuehttps://crawlee.dev를 가리키는 하나의 요청이 있습니다.

요청 일괄 추가

위 코드는 요청 큐의 개념을 설명하기 위한 것입니다. 곧 배우게 될 crawler.addRequests() 메서드를 사용하면 이 초기화 코드를 건너뛸 수 있으며, 많은 수의 요청도 중단 없이 추가할 수 있습니다.

CheerioCrawler 만들기

Crawlee에는 세 가지 주요 크롤러 클래스가 있습니다: CheerioCrawler, PuppeteerCrawler, PlaywrightCrawler. 이들에 대한 간단한 설명은 빠른 시작 강좌에서 확인할 수 있습니다.

특별한 이유가 없다면 먼저 CheerioCrawler로 시작하는 것이 좋습니다. HTTP2를 지원하는 HTTP 크롤러로, 차단 방지 기능과 Cheerio HTML 파서가 통합되어 있습니다. 빠르고 간단하며 운영 비용이 적고 복잡한 종속성이 필요하지 않습니다. 유일한 단점은 JavaScript 렌더링이 필요한 웹사이트에서는 바로 작동하지 않는다는 것입니다. 하지만 많은 현대 웹사이트가 서버 사이드 렌더링을 사용하므로 JavaScript 렌더링이 필요하지 않을 수 있습니다.

앞서 만든 RequestQueue 예제를 계속 이어가보겠습니다.

src/main.js
// CheerioCrawler 임포트 추가
import { RequestQueue, CheerioCrawler } from 'crawlee';

const requestQueue = await RequestQueue.open();
await requestQueue.addRequest({ url: 'https://crawlee.dev' });

// 크롤러를 생성하고 URL이 있는 큐와
// 페이지를 처리할 request handler를 추가합니다.
const crawler = new CheerioCrawler({
requestQueue,
// `$` 인자는 웹사이트의 파싱된 HTML이 포함된
// Cheerio 객체입니다.
async requestHandler({ $, request }) {
// Cheerio로 <title> 텍스트를 추출합니다.
// Cheerio API 문서를 참고하세요.
const title = $('title').text();
console.log(`"${request.url}"의 제목: ${title}`);
}
})

// 크롤러를 시작하고 완료될 때까지 기다립니다
await crawler.run();

예제를 실행하면 https://crawlee.dev의 제목이 로그에 출력됩니다. 실제로 일어나는 일은 CheerioCrawler가 먼저 https://crawlee.dev에 HTTP 요청을 보내고, 받은 HTML을 Cheerio로 파싱하여 requestHandler$ 인자로 제공하는 것입니다.

"https://crawlee.dev"의 제목: Crawlee · The scalable web crawling, scraping and automation library for JavaScript/Node.js | Crawlee.

더 빠른 요청 추가 방법

앞서 crawler.addRequests() 메서드를 사용하면 요청 큐 초기화를 건너뛸 수 있다고 했습니다. 매우 간단합니다. 모든 크롤러에는 암시적인 RequestQueue 인스턴스가 있으며, crawler.addRequests() 메서드로 요청을 추가할 수 있습니다. 더 나아가 crawler.run()의 첫 번째 매개변수를 사용할 수도 있습니다!

src/main.js
// RequestQueue 임포트가 더 이상 필요하지 않습니다
import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
async requestHandler({ $, request }) {
const title = $('title').text();
console.log(`"${request.url}"의 제목: ${title}`);
}
})

// 제공된 URL로 크롤러를 시작합니다
await crawler.run(['https://crawlee.dev']);

이 코드를 실행하면 앞서의 긴 예제와 정확히 같은 결과를 볼 수 있습니다. RequestQueue는 여전히 존재하지만, 크롤러가 자동으로 관리합니다.

정보

이 방법은 코드를 줄이는 것뿐만 아니라 성능도 향상시킵니다! 처리를 시작하기 전에 첫 1000개의 요청만 큐에 추가되기를 기다리므로 거의 즉시 시작됩니다. 그 후에는 나머지 요청을 백그라운드에서 계속 추가합니다(역시 1초마다 1000개씩 일괄 처리).

다음 단계

다음으로는 링크 크롤링에 대해 배워보겠습니다. 크롤링하는 페이지에서 새로운 URL을 찾아 크롤러가 방문할 수 있도록 RequestQueue에 추가하는 방법을 알아보겠습니다.