트러블 슈팅

[puppeteer] 크롤러 성능향상, 이미지 수신 차단

임채성 2023. 1. 2. 16:55

서론

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는 웹 페이지 접속이 잘 된다는 것을 보여줍니다!

위 GIF를 보시면 이미지를 로딩하느라 아래 이미지들은 가져오지 못하는 모습을 볼 수 있습니다.

 

 

이미지 차단하기

이미지를 차단하려면 요청 Interception을 설정해야합니다.

Interception: 통신 차단

await page.setRequestInterception(true);

page.on('request', (req) => {
    if(req.resourceType() === 'image'){ // 만약 요청 타입이 '이미지'라면
        req.abort(); // 거부
    }
    else { // 이미지가 아니라면
        req.continue(); // 수락
    }
});

 

요청을 기본적으로 차단하고, 웹 사이트에서 들어오는 요청을 수락할 지 거부할 지 설정할 수 있습니다.

 

실행하면 이렇게됩니다.

gif 렌더링 문제로 백그라운드가 깨지네요. 이미지는 잘 거부됩니다!

이미지만을 제외하고 웹 페이지를 잘 가져오고 있습니다!

 

결과 코드:

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(); // 수락
    }
});

이렇게 실행하면, 이렇게 작동합니다.

 

위 GIF는 CSS, 이미지, 폰트 스타일이 제거되었습니다.

 

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을 포함

참고한 소스코드

 

 

참고자료