본문으로 건너뛰기

더 많은 URL 추가하기

이전에 단일 페이지의 HTML을 다운로드하고 제목을 읽어 콘솔에 출력하는 간단한 크롤러를 만들었습니다. 다음은 원본 소스 코드입니다:

import { CheerioCrawler } from 'crawlee';

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

await crawler.run(['https://crawlee.dev']);

이제 이전 섹션의 예제를 기반으로 개선해보겠습니다. 더 많은 URL을 큐에 추가하여 크롤러가 계속 실행되면서 새로운 링크를 찾고, RequestQueue에 추가한 다음 스크래핑하도록 만들어보겠습니다.

크롤링 작동 방식

프로세스는 간단합니다:

  1. 페이지에서 새로운 링크를 찾습니다.
  2. 같은 도메인(이 경우 crawlee.dev)을 가리키는 링크만 필터링합니다.
  3. 이들을 RequestQueue에 추가(인큐)합니다.
  4. 새로 추가된 링크를 방문합니다.
  5. 이 과정을 반복합니다.

다음 단락에서는 크롤링을 단일 함수 호출로 단순화하는 enqueueLinks 함수에 대해 배워보겠습니다. 비교와 학습을 위해 두 번째 코드 탭에서는 enqueueLinks 없이 작성된 동등한 솔루션을 보여드리겠습니다.

enqueueLinks의 컨텍스트 인식

enqueueLinks 함수는 컨텍스트를 인식합니다. 즉, 현재 크롤링 중인 페이지에 대한 정보를 컨텍스트에서 읽어오므로 별도의 인자를 제공할 필요가 없습니다. Cheerio 함수 $를 사용하여 링크를 찾고 자동으로 실행 중인 크롤러의 RequestQueue에 링크를 추가합니다.

maxRequestsPerCrawl로 크롤링 제한하기

코드를 테스트하거나 크롤러가 수백만 개의 링크를 찾을 수 있는 경우, 크롤링할 페이지 수에 최대 제한을 설정하는 것이 매우 유용합니다. 이 옵션은 maxRequestsPerCrawl이라고 하며 모든 크롤러에서 사용할 수 있습니다:

const crawler = new CheerioCrawler({
maxRequestsPerCrawl: 20,
// ...
});

이는 20번째 요청이 완료된 후에는 새로운 요청이 시작되지 않음을 의미합니다. 병렬 처리로 인해 실제 처리된 요청 수는 약간 더 많을 수 있습니다. 실행 중인 요청은 강제로 중단되지 않기 때문입니다. 대부분의 경우 강제 중단은 불가능합니다.

새로운 링크 찾기

웹을 크롤링할 때 따라갈 링크를 찾는 방법은 여러 가지가 있습니다. 우리의 목적에는 href 속성이 있는 <a> 요소를 찾는 것이 가장 적합합니다. 예를 들면:

<a href="https://crawlee.dev/docs/introduction">Crawlee 소개로 이동하는 링크입니다</a>

이것이 가장 일반적인 경우이므로 enqueueLinks의 기본 동작이기도 합니다.

src/main.mjs
import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
// 크롤링을 제한하여 테스트를
// 더 짧고 안전하게 만듭니다.
maxRequestsPerCrawl: 20,
// enqueueLinks는 requestHandler의 매개변수입니다
async requestHandler({ $, request, enqueueLinks }) {
const title = $('title').text();
console.log(`"${request.url}"의 제목: ${title}`);
// enqueueLinks 함수는 컨텍스트를 인식하므로
// 매개변수가 필요하지 않습니다.
await enqueueLinks();
},
});

await crawler.run(['https://crawlee.dev']);

enqueueLinks의 기본 요소 선택을 변경해야 하는 경우 selector 인자를 사용할 수 있습니다.

await enqueueLinks({
selector: 'div.has-link'
});

같은 도메인의 링크 필터링하기

웹사이트는 일반적으로 원래 페이지에서 다른 곳으로 이동하는 많은 링크를 포함하고 있습니다. 이는 정상적인 현상이지만, 웹사이트를 크롤링할 때는 보통 해당 사이트만 크롤링하고 구글, 페이스북, 트위터 등으로 크롤러가 이동하지 않기를 원합니다. 따라서 다른 도메인으로 연결되는 링크를 필터링하고 같은 도메인의 링크만 유지해야 합니다.

src/main.mjs
import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
maxRequestsPerCrawl: 20,
async requestHandler({ $, request, enqueueLinks }) {
const title = $('title').text();
console.log(`"${request.url}"의 제목: ${title}`);
// enqueueLinks의 기본 동작은 같은 호스트네임에 머무는 것입니다.
// 따라서 매개변수가 필요하지 않습니다.
// 이는 서브도메인이 동일하게 유지되도록 합니다.
await enqueueLinks();
},
});

await crawler.run(['https://crawlee.dev']);

enqueueLinks의 기본 동작은 같은 호스트네임에 머무는 것입니다. 이는 서브도메인을 포함하지 않습니다. 크롤링에 서브도메인을 포함하려면 strategy 인자를 사용하세요.

await enqueueLinks({
strategy: 'same-domain'
});

코드를 실행하면 크롤러가 첫 페이지의 제목을 로그로 출력하고, URL 수를 보여주는 인큐잉 메시지가 표시된 다음, 첫 번째로 인큐된 페이지의 제목이 표시되는 등의 과정이 반복됩니다.

중복 URL 건너뛰기

중복 URL을 건너뛰는 것은 매우 중요합니다. 같은 페이지를 여러 번 방문하면 중복된 결과가 생기기 때문입니다. 이는 RequestQueue가 요청의 uniqueKey를 사용하여 자동으로 처리합니다. 이 uniqueKey는 요청의 URL을 소문자로 변환하고, 쿼리 매개변수를 사전순으로 정렬하고, 프래그먼트를 제거하는 등의 처리를 통해 자동으로 생성되어 큐에 고유한 URL만 포함되도록 합니다.

고급 필터링 인자

enqueueLinks의 기본값이 필요한 것과 정확히 일치하는 경우가 많지만, 어떤 URL을 인큐할지 세밀하게 제어할 수도 있습니다. 앞서 언급한 EnqueueStrategy가 한 가지 방법입니다. All 전략을 사용하면 도메인에 관계없이 모든 링크를 따라갈 수 있고, SameDomain 전략으로 같은 도메인 이름을 대상으로 하는 링크를 인큐할 수 있습니다.

await enqueueLinks({
strategy: 'all', // 인터넷을 자유롭게 탐색
});

패턴으로 URL 필터링

더 세밀한 제어를 위해 globs, regexps, pseudoUrls를 사용하여 URL을 필터링할 수 있습니다. 각 인자는 항상 Array이지만, 내용은 다양한 형태를 가질 수 있습니다. 이들과 다른 옵션에 대한 자세한 정보는 레퍼런스를 참조하세요.

기본값 재정의

이러한 옵션 중 하나를 제공하면, 기본 same-hostname 전략은 옵션에서 명시적으로 설정하지 않는 한 적용되지 않습니다.

await enqueueLinks({
globs: ['http?(s)://apify.com/*/*'],
});

요청 변환

완벽한 제어를 위해 transformRequestFunction이 있습니다. 새로운 Request가 생성되어 RequestQueue에 인큐되기 직전에 이 함수를 사용하여 건너뛰거나 내용을 수정할 수 있습니다. 이는 큐에 같은 URL을 가진 여러 요청을 추가해야 하지만 메서드나 페이로드가 다른 경우에 유용합니다. 또 다른 사용 사례는 userData를 동적으로 업데이트하거나 생성하는 것입니다.

await enqueueLinks({
globs: ['http?(s)://apify.com/*/*'],
transformRequestFunction(req) {
// `.pdf`로 끝나는 모든 링크 무시
if (req.url.endsWith('.pdf')) return false;
return req;
},
});

이것으로 끝입니다! enqueueLinks()는 Crawlee의 강력한 헬퍼 함수의 한 예일 뿐입니다. 이러한 도구들은 모두 귀찮은 크롤링 관리는 도구에 맡기고 데이터 수집에 집중할 수 있도록 설계되었습니다.

다음 단계

다음으로는 실제 웹사이트를 스크래핑하는 프로젝트를 시작하고 Crawlee의 더 많은 기능을 배워보겠습니다.