서론
NESTJS에서 커뮤니티 크롤러를 만들던 중 용량이 큰 자료가 많은 스압(스크롤 압박) 글들 때문에 크롤러가 Timeout Error가 뜨는 일이 발생했다.
생각해보니, 불필요한 이미지는 수신할 필요가 없었다.
JS 크롤러 라이브러리인 puppeteer을 이용해서 효율적으로 크롤링을 진행해보려고 한다.
사용 스택
- Nest.JS 9 // 무관
- es2017
- puppeteer 19.4.1
(준비하기..)
혹시 설치도 안되어있다면 설치하자..
yarn add puppeteer
시작하기
import * as puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
await page.goto('https://www.coupang.com/');
한 라인씩 봅시다.
import * as puppeteer from 'puppeteer';
설치한 puppeteer를 가져오는 코드입니다. 여기서 노란줄 등의 문제가 생긴다면 제대로 설치되지 않은 것입니다.
const browser = await puppeteer.launch({ headless: false });
우리가 사용하는 웹 브라우저를 실행합니다.
headless 모드를 껐습니다.
headless 모드는 백그라운드에서 실행되는 모드입니다.
개발 후에는 다른 활동을 같이 할 수 있기에 편하지만, 처음 개발할 때는 어디서 버그 잡기 편하게 headless 모드를 꺼주겠습니다.
const page = await browser.newPage();
새로운 페이지를 만들고, page라는 변수에 저장합니다.
await page.setViewport({ width: 1920, height: 1080 });
반응형 웹사이트는 페이지 크기에 맞춰서 다르게 작동하기 때문에 페이지의 해상도를 설정해주겠습니다.
await page.goto('https://nomadcoders.co/');
페이지를 이 링크로 이동하게 하는 코드입니다.
실행하면 이렇게 나오는지 확인해봅시다.
위 GIF를 보시면 이미지를 로딩하느라 아래 이미지들은 가져오지 못하는 모습을 볼 수 있습니다.
이미지 차단하기
이미지를 차단하려면 요청 Interception을 설정해야합니다.
Interception: 통신 차단
await page.setRequestInterception(true);
page.on('request', (req) => {
if(req.resourceType() === 'image'){ // 만약 요청 타입이 '이미지'라면
req.abort(); // 거부
}
else { // 이미지가 아니라면
req.continue(); // 수락
}
});
요청을 기본적으로 차단하고, 웹 사이트에서 들어오는 요청을 수락할 지 거부할 지 설정할 수 있습니다.
실행하면 이렇게됩니다.
이미지만을 제외하고 웹 페이지를 잘 가져오고 있습니다!
결과 코드:
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
await page.goto('https://nomadcoders.co/');
await page.setRequestInterception(true);
page.on('request', (req) => {
if (
req.resourceType() === 'image'
) {
// 만약 요청 타입이 '이미지'라면
req.abort(); // 거부
} else {
// 이미지가 아니라면
req.continue(); // 수락
}
});
CSS 차단하기
프론트엔드 개발자가 웹을 잘 꾸며놨지만, 크롤러 입장에는 속도만 느려지는 방해요소일 뿐입니다. 제거하죠
await page.setRequestInterception(true);
page.on('request', (req) => {
if(
req.resourceType() === 'image' ||
req.resourceType() === 'stylesheet' ||
req.resourceType() === 'font'
){ // 만약 요청 타입이 '이미지' or 'CSS' or '폰트' 라면
req.abort(); // 거부
}
else { // 아니라면
req.continue(); // 수락
}
});
이렇게 실행하면, 이렇게 작동합니다.
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
await page.goto('https://nomadcoders.co/');
await page.setRequestInterception(true);
page.on('request', (req) => {
if (
req.resourceType() === 'image' ||
req.resourceType() === 'font' ||
req.resourceType() === 'stylesheet'
) {
// 만약 요청 타입이 '이미지' or 'CSS' or '폰트' 라면
req.abort(); // 거부
} else {
// 이미지가 아니라면
req.continue(); // 수락
}
});
Resource Type에 대한 설명
HTTP 송수신에 사용하는 mimeType과 닮아있습니다.
자주 사용할만한 리소스 타입들에 대한 설명을 적어두겠습니다.
설명 | mimeType | |
document | html | text/html |
stylesheet | css, xsl | text/css |
image | jpeg, jpg, svg, gif, png, ico, tiff, tif, bmp | image/로 시작하는 파일 |
media | webp | - |
font | ttf, otf, ttc, woff | font |
script | javaScript | - text/ 로 시작하는 파일 (text/html, text/css는 제외) - script, application을 포함 |
참고자료
'트러블 슈팅' 카테고리의 다른 글
팀원이 설정한 알람이 내 폰에서 울려야한다. (0) | 2023.03.28 |
---|---|
어떨 때 composite key를 사용해야 할까? (0) | 2023.03.19 |
OAuth 로그인을 하는데 새로운 계정이 생성되면 안된다. (0) | 2023.02.23 |
[Node.js] 싱글 스레드 언어가 어떻게 비동기 처리가 가능한가?, Node.js 동작 원리 (0) | 2023.01.26 |
[NestJS] AdminJS를 이용한 크롤러 관리 패널 제작기 (0) | 2023.01.14 |
글 내용 중 잘못되거나 이해되지 않는 부분은 댓글을 달아주세요! 감사합니다! 문의: puleugo@gmail.com