본문으로 건너뛰기

스토어 크롤링하기

예제 창고 스토어의 모든 데이터를 크롤링하려면, 먼저 모든 제품이 있는 페이지를 방문해야 합니다. 즉, 사용 가능한 모든 카테고리와 제품 상세 페이지를 살펴봐야 합니다.

목록 페이지 크롤링하기​

이전 강의에서는 enqueueLinks() 함수를 다음과 같이 사용했습니다:

await enqueueLinks();

이 방식이 당시에는 유용했지만, 이제는 다른 방법이 필요합니다. 같은 호스트네임을 가진 모든 <a href=".."> 요소의 링크를 찾는 대신, 크롤러가 다음 결과 페이지로 이동하게 해주는 특정 링크만 찾아야 합니다. 그렇지 않으면 크롤러가 관심 없는 많은 다른 페이지를 방문하게 됩니다. DevTools의 도움과 enqueueLinks()의 또 다른 매개변수를 사용하면 이는 꽤 쉽게 해결됩니다.

import { PlaywrightCrawler } from 'crawlee';

const crawler = new PlaywrightCrawler({
requestHandler: async ({ page, request, enqueueLinks }) => {
console.log(`처리 중: ${request.url}`);

// 이 로직은 메인 카테고리 목록에서만 실행하고,
// 하위 페이지에서는 실행하지 않습니다.
if (request.label !== 'CATEGORY') {

// 카테고리 카드가 렌더링될 때까지 기다립니다.
// 그렇지 않으면 enqueueLinks가 아무것도 인큐하지 않습니다.
await page.waitForSelector('.collection-block-item');

// 제공된 선택자와 일치하는 요소에서만
// 링크를 큐에 추가합니다.
await enqueueLinks({
selector: '.collection-block-item',
label: 'CATEGORY',
});
}
},
});

await crawler.run(['https://warehouse-theme-metal.myshopify.com/collections']);

이 코드는 꽤 익숙해 보일 것입니다. 현재 처리 중인 URL을 콘솔에 로그로 출력하고 더 많은 링크를 인큐하는 매우 간단한 requestHandler입니다. 하지만 몇 가지 새롭고 흥미로운 추가 사항이 있습니다. 하나씩 살펴보겠습니다.

enqueueLinks()의 selector 매개변수​

이전에 enqueueLinks()를 사용할 때는 selector 매개변수를 제공하지 않았고, 기본값인 a(모든 <a> 요소를 찾음)를 사용했기 때문에 문제가 없었습니다. 하지만 이제는 더 구체적이어야 합니다. 카테고리 페이지에는 여러 <a> 링크가 있고, 크롤러가 사용 가능한 결과 목록으로 이동하게 해주는 링크만 필요합니다. DevTools를 사용하면 .collection-block-item 선택자로 필요한 링크를 선택할 수 있다는 것을 알 수 있습니다. 이 선택자는 class=collection-block-item 속성이 있는 모든 요소를 선택합니다.

enqueueLinks()의 label​

Crawlee에서는 label을 자주 볼 수 있습니다. 이는 나중에 빠르게 식별할 수 있도록 Request 인스턴스에 라벨을 붙이는 편리한 방법입니다. request.label로 접근할 수 있으며 string 타입입니다. 원하는 대로 요청의 이름을 지정할 수 있습니다. 여기서는 제품 카테고리를 나타내는 페이지를 인큐하고 있다는 것을 표시하기 위해 CATEGORY 라벨을 사용했습니다. enqueueLinks() 함수는 RequestQueue에 인큐하기 전에 이 라벨을 모든 요청에 추가합니다. 이것이 왜 유용한지는 곧 알게 될 것입니다.

상세 페이지 크롤링하기​

비슷한 방식으로, 필요한 모든 데이터를 스크래핑할 수 있는 제품 상세 페이지의 모든 URL도 수집해야 합니다. 다음 코드는 이미 알고 있는 개념을 다른 링크 세트에 적용한 것입니다.

import { PlaywrightCrawler } from 'crawlee';

const crawler = new PlaywrightCrawler({
requestHandler: async ({ page, request, enqueueLinks }) => {
console.log(`처리 중: ${request.url}`);
if (request.label === 'DETAIL') {
// 아직 상세 정보는 처리하지 않습니다.
} else if (request.label === 'CATEGORY') {
// 이제 카테고리 페이지에 있습니다. 이를 통해 페이지네이션을 하면서
// 모든 제품과 발견하는 후속 페이지를 인큐할 수 있습니다.

await page.waitForSelector('.product-item > a');
await enqueueLinks({
selector: '.product-item > a',
label: 'DETAIL', // <= 다른 라벨에 주목하세요
});

// 이제 "다음" 버튼을 찾아서 다음 결과 페이지를 인큐해야 합니다(있는 경우)
const nextButton = await page.$('a.pagination__next');
if (nextButton) {
await enqueueLinks({
selector: 'a.pagination__next',
label: 'CATEGORY', // <= 같은 라벨에 주목하세요
});
}
} else {
// 이는 라벨이 없는 시작 페이지라는 의미입니다.
// 이 페이지에서는 모든 카테고리 페이지만 인큐하면 됩니다.

await page.waitForSelector('.collection-block-item');
await enqueueLinks({
selector: '.collection-block-item',
label: 'CATEGORY',
});
}
},
});

await crawler.run(['https://warehouse-theme-metal.myshopify.com/collections']);

이제 크롤링 코드가 완성되었습니다. 코드를 실행하면 크롤러가 모든 목록 URL과 모든 상세 URL을 방문하는 것을 볼 수 있습니다.

다음 단계​

이것으로 크롤링 강의를 마칩니다. 크롤러가 필요한 모든 페이지를 방문하도록 가르쳤기 때문입니다. 이제 데이터 스크래핑을 계속해보겠습니다.