React 프로젝트에서 웹 스크래핑 하기 위해 puppeteer, cheerio 활용하기

우디·2024년 3월 4일
0
post-thumbnail

안녕하세요:) 개발자 우디입니다! 아래 내용 관련하여 작업 중이신 분들께 도움이되길 바라며 글을 공유하니 참고 부탁드립니다😊
(이번에 벨로그로 이사오면서 예전 글을 옮겨적었습니다. 이 점 양해 부탁드립니다!)

작업 시점: 2022년 7월

배경

  • 베타서비스 제공 과정에서 트위치 카테고리 정보를 일일이 찾아서 입력해줘야 했음 → 이를 개선하기 위해 자동으로 카테고리 받아오는 기능을 구현함.
  • 원래는 트위치 api를 활용하려고 했는데, 외부 서버를 따로 구축해야 해서 리소스가 클 것으로 예상되어 데이터 스크래핑 방식으로 구현함.
    • 스크래핑은 실제 팀원들이 검토할 때 참고하는 트위치 트래커 사이트를 대상으로 했음.
  • 웹 스크래핑 작업을 위해 puppeteer와 cheerio를 활용했음.

puppeteer 관련 내용

  • Puppeteer 는 Headless Chrome 혹은 Chromium 를 제어하도록 도와주는 라이브러리. 필요한 데이터가 JavaScript에 의해 동적으로 생성되는 경우에 유용함.

    • puppeteer 기능
      • SPA(Single Page Application) 화면의 렌더링 가능.
      • 렌더링 후 키보드, 마우스 입력 제어 가능.
      • 웹페이지의 자동 테스트 도구 만들기 가능.
      • 각각의 웹페이지 crawling 이 가능
      • 접속한 페이지를 스크린샷을 찍거나 PDF로 만들 수 있음.
    • Headless Browser
      • CLI (Command Line interface) 에서 작동하는 브라우저.
      • 일반적으로 사용자가 사용하는 GUI 에서 동작하는 브라우저가 아님.
      • 백그라운드에서 동작하며, 일반적인 브라우저와 동일하게 웹페이지에 접속하여 HTML, CSS로 DOM Tree를 만들고, JS 엔진을 구동함.
      • 유일한 차이점은 만든 화면을 사용자에게 보여주지 않는다는 점.
      • 일반 브라우저와 큰 차이가 없기 때문에 보여주는 화면이 없이도, 화면 테스트나 스크린샷을 찍는것등 다양한 기능 동작이 가능하며, 사용자가 실제 사용하는 환경과 비슷하게 테스트도 가능.
      • puppeteer에서는 옵션 설정을 통해 headless 모드와 non-headless 모드 둘 다 사용 가능.
  • puppeteer.launch() 활용하여 브라우저 생성.

  • browser.newPage() 활용하여 새 페이지 생성.

  • setViewport() 활용하여 페이지 크기 설정.

  • streamDetailUrl 로 이동 원하는 URL 구성

  • goto() 활용하여 원하는 URL로 이동.

    • wait 옵션을 지정해주는 waitUntil 속성
  • content() 활용하여 페이지에서 콘텐츠 받아오기.

  • 페이지 콘텐츠에 restricted 가 포함되었는지를 통해 에러 발생 여부 파악.

  • 실제 코드 적용

    const browser = await puppeteer.launch();
    
    const streamDetailPage = await browser.newPage();
    
    await streamDetailPage.setViewport({
      width: 1366,
      height: 768,
    });
    
    const streamDetailUrl = `https://twitchtracker.com/${streamerLoginId}/streams/${streamId}`;
    
    ...
    
    await streamDetailPage.goto(streamDetailUrl, {
      waitUntil: 'load',
    });
    
    const streamDetailPageContent = await streamDetailPage.content();
    
    // when access restricted (error 429)
    if (streamDetailPageContent.includes('restricted')) { // 
      finalCategoryResult = {
        // { name: 'Project Zomboid', endTime: 22980, startTime: 5880 }
        data: '',
        twitchTrackerUrl: streamDetailUrl,
        error: 'Error occurs while getting category info: Access restricted (Error 429)',
      };
      return finalCategoryResult;
    }

cheerio 관련 내용

  • cheerio는 Node.js에서 HTML을 파싱하여 스크래핑 하기 위한 라이브러리.

  • 앞서 puppeteer에서 받아온 페이지 콘텐츠를 cheerio.load() 활용하여 로드한 후, 원하는 방식으로 데이터 가공함.

  • 실제 코드 적용

    const $streamDetailPage = cheerio.load(streamDetailPageContent);
    
    const chartLabels = $streamDetailPage('span.highcharts-plot-line-label');
    
    let chartLabelTextArr = [];
    chartLabels.each(function (idx, label) {
      chartLabelTextArr.push($streamDetailPage(label).text());
    });

스크래핑 결과

  • 자동으로 카테고리 정보를 스크랩 해줘서 작업 시간을 줄일 수 있었음.

배우고 느낀 점

  • 웹 페이지 구조가 다소 복잡해서 테스트가 오래 걸렸다.
  • 입력 장치 제어, 사이트 이동 등 사람이 하는 작업을 자동으로 할 수 있다는 점이 흥미로웠다.
profile
넓고 깊은 지식을 보유한 개발자를 꿈꾸고 있습니다:) 기억 혹은 공유하고 싶은 내용들을 기록하는 공간입니다

0개의 댓글