요청 저장소
Crawlee는 특정 작업에 유용한 여러 요청 저장소 유형을 제공합니다. 요청들은 CRAWLEE_STORAGE_DIR
환경 변수로 정의된 로컬 디렉토리에 저장됩니다. 이 변수가 정의되지 않은 경우, Crawlee는 기본적으로 현재 작업 디렉토리의 ./storage
를 CRAWLEE_STORAGE_DIR
로 설정합니다.
요청 큐
요청 큐는 크롤링할 URL들을 저장하는 공간입니다. 이 큐는 몇 개의 URL로 시작하여 다른 페이지의 링크를 재귀적으로 따라가는 웹사이트의 심층 크롤링에 사용됩니다. 이 데이터 구조는 너비 우선 및 깊이 우선 크롤링 순서를 모두 지원합니다.
각 Crawlee 프로젝트 실행은 기본 요청 큐와 연결됩니다. 일반적으로 특정 크롤러 실행에서 크롤링할 URL을 저장하는 데 사용되며, 선택적으로 사용할 수 있습니다.
Crawlee에서 요청 큐는 RequestQueue
클래스로 표현됩니다.
요청 큐는 MemoryStorage
클래스에 의해 관리되며, 데이터는 메모리에 저장되면서 동시에 CRAWLEE_STORAGE_DIR
환경 변수로 지정된 로컬 디렉토리에 다음과 같이 저장됩니다:
{CRAWLEE_STORAGE_DIR}/request_queues/{QUEUE_ID}/entries.json
{QUEUE_ID}
는 요청 큐의 이름 또는 ID입니다. CRAWLEE_DEFAULT_REQUEST_QUEUE_ID
환경 변수로 재정의하지 않는 한, 기본 큐의 ID는 default
입니다.
entries.json
은 요청들의 배열을 포함합니다.
다음 코드는 요청 큐의 사용법을 보여줍니다:
- 크롤러에서 사용
- 크롤러에서 명시적 사용
- 기본 작업
import { CheerioCrawler } from 'crawlee';
// 크롤러는 큐에서 요청을 자동으로 처리합니다.
// Puppeteer/Playwright 크롤러에 대해 동일한 방식으로 사용됩니다.
const crawler = new CheerioCrawler({
// 여기서는 요청 큐를 지정하지 않습니다
async requestHandler({ $, crawler, enqueueLinks }) {
// 새 요청을 큐에 추가
await crawler.addRequests([{ url: 'https://example.com/new-page' }]);
// 페이지에서 찾은 링크를 큐에 추가
await enqueueLinks();
},
});
// 초기 요청 추가
// 여기서는 요청 큐를 명시적으로 열지 않습니다
await crawler.addRequests([
{ url: 'https://example.com/1' },
{ url: 'https://example.com/2' },
{ url: 'https://example.com/3' },
// ...
]);
// 크롤러 실행
await crawler.run();
import { RequestQueue, CheerioCrawler } from 'crawlee';
// 현재 실행에 연결된 기본 요청 큐 열기
const requestQueue = await RequestQueue.open();
// 초기 요청 추가
await requestQueue.addRequests([
{ url: 'https://example.com/1' },
{ url: 'https://example.com/2' },
{ url: 'https://example.com/3' },
// ...
]);
// 크롤러는 큐에서 요청을 자동으로 처리합니다.
// Puppeteer/Playwright 크롤러에 대해 동일한 방식으로 사용됩니다.
const crawler = new CheerioCrawler({
requestQueue,
async requestHandler({ $, request, enqueueLinks }) {
// 새 요청을 큐에 추가
await requestQueue.addRequests([{ url: 'https://example.com/new-page' }]);
// 페이지에서 찾은 링크를 큐에 추가
await enqueueLinks();
},
});
// 크롤러 실행
await crawler.run();
import { RequestQueue } from 'crawlee';
// 크롤러 실행에 연결된 기본 요청 큐 열기
const requestQueue = await RequestQueue.open();
// 초기 요청 배치 추가 (하나의 배열이 될 수 있음)
await requestQueue.addRequests([
{ url: 'https://example.com/1' },
{ url: 'https://example.com/2' },
{ url: 'https://example.com/3' },
]);
// 명명된 요청 큐 열기
const namedRequestQueue = await RequestQueue.open('named-queue');
// 명명된 요청 큐 삭제
await namedRequestQueue.drop();
크롤러에서 요청 큐를 사용하는 더 자세한 예제는 Puppeteer 크롤러 예제를 참조하세요.
요청 리스트
요청 리스트는 엄밀히 말해 저장소가 아닙니다 - 크롤러 실행 메모리에 저장된 크롤링할 URL 목록을 나타냅니다(선택적으로 실행과 연결된 기본 키-값 저장소에 저장할 수 있습니다). 이 리스트는 크롤러가 방문해야 할 모든 URL을 미리 알고 있고 실행 중에 URL이 추가되지 않을 때 많은 수의 URL을 크롤링하는 데 사용됩니다. URL은 코드에서 직접 제공하거나 웹에 호스팅된 텍스트 파일에서 파싱할 수 있습니다.
요청 리스트는 코드에서 명시적으로 지정한 경우에만 크롤러 실행을 위해 생성됩니다. 사용은 선택사항입니다.
Crawlee에서 요청 리스트는 RequestList
클래스로 표현됩니다.
다음 코드는 요청 리스트의 기본 작업을 보여줍니다:
import { RequestList, PuppeteerCrawler } from 'crawlee';
// 방문할 URL이 있는 sources 배열 준비
const sources = [
{ url: 'http://www.example.com/page-1' },
{ url: 'http://www.example.com/page-2' },
{ url: 'http://www.example.com/page-3' },
];
// 요청 리스트 열기
// 리스트 이름은 소스와 리스트 상태를 키-값 저장소에 유지하는 데 사용됩니다
const requestList = await RequestList.open('my-list', sources);
// 크롤러는 자동으로 리스트의 요청을 처리합니다
// Cheerio/Playwright 크롤러에서도 동일한 방식으로 사용됩니다
const crawler = new PuppeteerCrawler({
requestList,
async requestHandler({ page, request }) {
// 페이지 처리 (데이터 추출, 스크린샷 촬영 등)
// 여기서는 요청 리스트에 더 이상 요청을 추가할 수 없습니다
},
});
어떤 것을 선택해야 할까요?
요청 큐를 사용할 때는 일반적으로 몇 개의 시작 URL(예: 이커머스 웹사이트의 카테고리 페이지)이 있고 프로그래밍 방식으로 더 많은 URL(예: 개별 상품 페이지)을 재귀적으로 큐에 추가합니다. 요청의 동적 추가와 제거를 지원합니다. 요청 리스트는 초기화 후에는 변경할 수 없어서 URL을 추가하거나 제거할 수 없습니다.
반면에 요청 큐는 많은 수의 URL을 일괄적으로 추가하거나 제거하는 데 최적화되어 있지 않습니다. 기술적으로는 가능하지만, 요청이 큐에 하나씩 추가되므로 많은 수의 요청이 있을 경우 상당한 시간이 걸립니다. 하지만 요청 리스트는 수백만 개의 URL도 포함할 수 있으며, 큐에 비해 추가하는 데 걸리는 시간이 훨씬 적습니다.
요청 큐와 요청 리스트는 동일한 크롤러에서 함께 사용할 수 있습니다. 이 경우 요청 리스트의 각 요청은 먼저 요청 큐의 맨 앞에 추가된 후(요청 큐가 비어있지 않더라도) 큐에서 처리됩니다. 이는 동일한 URL이 두 번 처리되는 것을 방지하기 위해 필요합니다(리스트에서 한 번, 큐에서 한 번). 실제로 이러한 조합은 초기 URL이 많고 크롤러가 동적으로 더 많은 URL을 추가할 때 유용할 수 있습니다.
Crawlee에서는 요청 큐와 요청 리스트를 함께 사용할 필요성이 크게 줄었습니다(기술적으로는 가능하지만).
이전에는 초기 요청을 일괄적으로 큐에 추가할 방법이 없었습니다(요청 배열을 추가하는 것). 즉, addRequest()
함수를 사용하여 요청을 하나씩만 큐에 추가할 수 있었습니다.
하지만 이제는 요청을 일괄적으로 추가하는 addRequests()
함수를 사용할 수 있습니다. 따라서 요청 큐와 요청 리스트를 조합하는 대신, 이러한 사용 사례에는 요청 큐만 사용할 수 있습니다. 아래 예제를 참조하세요.
- 요청 큐
- 요청 큐 + 요청 리스트
// 이 방법을 권장합니다.
// 요청 목록을 사용하지 않으며, 여기서는 요청 큐를 명시적으로 사용하지 않습니다.
import { PuppeteerCrawler } from 'crawlee';
// 방문할 URL을 포함하는 소스 배열 준비 (수백만 개의 URL을 포함할 수 있음)
const sources = [
{ url: 'http://www.example.com/page-1' },
{ url: 'http://www.example.com/page-2' },
{ url: 'http://www.example.com/page-3' },
// ...
];
// 크롤러는 큐에서 요청을 자동으로 처리합니다.
// Cheerio/Playwright 크롤러에 대해 동일한 방식으로 사용됩니다
const crawler = new PuppeteerCrawler({
async requestHandler({ crawler, enqueueLinks }) {
// 새 요청을 큐에 추가
await crawler.addRequests(['http://www.example.com/new-page']);
// 페이지에서 찾은 링크를 큐에 추가
await enqueueLinks();
// 위의 요청은 큐에 추가되지만 목록에는 추가되지 않습니다
// 목록이 비어있으면 요청이 처리됩니다
// 여기서는 목록에 더 이상 요청을 추가할 수 없습니다
},
});
// 초기 소스 배열을 큐에 추가하고 크롤러 실행
await crawler.run(sources);
// 이 코드는 기술적으로 정확하지만
// 요청 큐와 요청 목록을 명시적으로 열어야 합니다.
// 요청 큐와 요청 목록을 사용하고 요청을 일괄 추가하는 것을 권장합니다.
import { RequestList, RequestQueue, PuppeteerCrawler } from 'crawlee';
// 방문할 URL을 포함하는 소스 배열 준비 (수백만 개의 URL을 포함할 수 있음)
const sources = [
{ url: 'http://www.example.com/page-1' },
{ url: 'http://www.example.com/page-2' },
{ url: 'http://www.example.com/page-3' },
// ...
];
// 초기 소스 배열을 사용하여 요청 목록 열기
const requestList = await RequestList.open('my-list', sources);
// 기본 요청 큐 열기. 큐에 요청을 추가할 필요는 없습니다
const requestQueue = await RequestQueue.open();
// 크롤러는 목록과 큐에서 요청을 자동으로 처리합니다.
// Cheerio/Playwright 크롤러에 대해 동일한 방식으로 사용됩니다
const crawler = new PuppeteerCrawler({
requestList,
requestQueue,
// 요청 목록에서 요청은 하나씩 큐에 추가됩니다.
// 이 시점에서 동일한 URL이 목록과 큐에 모두 존재합니다
async requestHandler({ crawler, enqueueLinks }) {
// 새 요청을 큐에 추가
await crawler.addRequests(['http://www.example.com/new-page']);
// 페이지에서 찾은 링크를 큐에 추가
await enqueueLinks();
// 위의 요청은 큐에 추가되지만 목록에는 추가되지 않습니다
// 목록이 비어있으면 요청이 처리됩니다
// 여기서는 목록에 더 이상 요청을 추가할 수 없습니다
},
});
// 크롤러 실행
await crawler.run();
저장소 정리하기
달리 지정하지 않는 한 기본 저장소는 크롤러가 시작되기 전에 정리됩니다. 이는 저장소를 열 때(RequestQueue.open()
을 통해) 또는 기본 저장소를 헬퍼 메서드 중 하나를 통해 사용하려고 할 때(내부적으로 RequestQueue.open()
을 호출하는 crawler.addRequests()
등) 발생합니다. 코드에서 저장소를 명시적으로 사용하지 않는 경우, 정리는 결국 크롤러의 run
메서드가 실행될 때 발생합니다. 저장소를 더 일찍 정리해야 하는 경우 purgeDefaultStorages()
헬퍼를 명시적으로 사용할 수 있습니다:
import { purgeDefaultStorages } from 'crawlee';
await purgeDefaultStorages();
이 함수를 호출하면 기본 요청 저장소 디렉토리(그리고 기본 키-값 저장소에 저장된 요청 리스트도)가 정리됩니다. 이는 StorageClient
인터페이스의 (선택적) purge
메서드를 실행하는 단축키입니다. 다시 말해, 현재 사용 중인 기본 저장소 구현의 purge
메서드를 호출합니다. options
객체에서 onlyPurgeOnce
를 true
로 설정하면 주어진 실행 컨텍스트에서 저장소가 한 번만 정리되도록 할 수 있습니다.