본문 바로가기
[IT] Node.JS

[Node.js] Express Rest API + Puppeteer 웹크롤링 서비스 VSCode Docker 이미지 생성 및 실행

by 오리엔탈킴 2022. 3. 9.

안녕하세요, 이전 포스팅들에서 Node.js Puppeteer 웹크롤링을 이용하여 데이터를 가져오고, 가져온 데이터를 Express를 이용하여 전달하는 Rest API 서비스를 개발하였습니다. 여러 포스팅으로 올려서 좀 정신이 없어 최종적으로 정리를 하고 마지막으로 VSCode에 Docker Extension을 설치하여 Docker관련 커맨드를 하나도 몰라도 쉽게 Docker 컨테이너 이미지를 생성하고 실행하는 내용을 정리해보도록 하겠습니다.

Express 프로젝트 생성

[[IT] Node.JS/[웹크롤링] Puppeteer] - [Node.js] Express 설치, 프로젝트 생성 및 Rest API 서버 만들기 (+웹크롤링 데이터 전달)

VSCode를 실행하여, 프로젝트를 생성할 폴더를 오픈하고, 터미널 창에서 아래의 커맨드를 차례로 입력하여 세팅된 Express 프로젝트를 생성합니다. 좀 더 상세한 설명과 내용이 필요하시면 위의 포스트를 참고하여 주세요~

  • npm init
  • npm i puppeteer
  • npm i express -save
  • npm install express-generator -g
  • express

 

Puppeteer 웹크롤링 구현

[[IT] Node.JS/[웹크롤링] Puppeteer] - [웹 크롤링] Node.js Puppeteer - investing 원자재(금은/천연가스/원유) 가격 가져오기

위의 포스팅에 구현한 예제 그대로 investing.com에서 원자재 가격을 실시간으로 웹 크롤링하여 가져오도록 하는 예제를 작성해보겠습니다. investing.com의 각 원자재 가격 페이지에 들어가면 아래와 같이 실시간 가격 부분이 있는데, 크롬의 F12 개발자 도구를 통해서 보면 해당 부분이 아래의 Selector 값으로 선택이 되어진다는 것을 확인하였습니다.

  • div[data-test="instrument-header-details"] span[data-test="instrument-price-last"]

원자재 가격 영역
가격 영역

Puppeteer로 Healess 브라우저를 이용하여 investing.com에 접속해서 원자재의 실시간 가격을 크롤링하는 예제는 아래와 같이 작성할 수 있습니다. 이 코드를 위에 생성한 Express 프로젝트에 service 폴더를 생성하고 그 아래 crawlingSvc.js 파일 생성하여 이 파일에 작성합니다. 이전 포스팅에서 구현한 부분과 다른 점은 puppeteer.launchargs: ['--no-sandbox', '--headless', '--disable-gpu'] 라는 옵션을 줘서 리눅스 기반 OS에서 Chrominum을 정상적으로 실행할 수 있도록 하였습니다.

const puppeteer = require('puppeteer');

// 각 원자재 이름과 URL 정보가 있는 MAP을 생성
const urlMap = new Map([
  ['silver','https://kr.investing.com/commodities/silver'],
  ['wti','https://kr.investing.com/commodities/crude-oil'],
  ['gas','https://kr.investing.com/commodities/natural-gas']
]);

let getCommodityPrice = async function(comdiName) { 
  // puppeteer 실행
  const browser = await puppeteer.launch({ 
    args: ['--no-sandbox', '--headless', '--disable-gpu']
    });
  const page = await browser.newPage(); 

  // 매개변수가 all이면 전체 map을 수행, 아니면 해당 원자재만 수행
  var resultList = new Array();
  if(comdiName === 'all'){
    let urlKeys = urlMap.keys();
    for(var v of urlKeys){
        await getPrice(v);
    }
  }else {
    await getPrice(comdiName);
  }

  async function getPrice(v){
    // map에 저장된 url 가져와서 방문
    var url = urlMap.get(v);
    await page.goto(url);

    // 가격 보여지는 DIV가 페이지 나타나면, 가격 값을 가져 옴
    await page.waitForSelector('div[data-test="instrument-header-details"]'); 
    let comdiPrice = await page.$eval('div[data-test="instrument-header-details"] span[data-test="instrument-price-last"]', x=> x.innerHTML);
  
    console.log(v,':',comdiPrice,'$');
    // Json 형태로 결과를 생성
    resultList.push({
        name:v,
        price:comdiPrice
    });
  }

  await browser.close();

  return resultList;
}
 // 다른 js 파일에서 참조하기 위해 Export 설정
module.exports.getCommodityPrice = getCommodityPrice;

 

Express Rest API 구현

[[IT] Node.JS/[웹크롤링] Puppeteer] - [Node.js] Express 설치, 프로젝트 생성 및 Rest API 서버 만들기 (+웹크롤링 데이터 전달)

자동으로 생성된 routes/index.js 파일에 아래와 같이 getPrice라는 URI를 가진 API를 생성해줍니다. 이 API를 실행 시 위에서 작성한 Puppeteer 메서드를 실행하여 investing.com의 실시간 원자재 가격을 가져와 응답할 수 있도록 구현해줍니다.

var express = require('express');
var router = express.Router();

// crawlingSvc.js 파일 import
var crawlingSvc = require('../service/crawlingSvc.js');

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

router.get('/getPrice', async function(req, res) {
  var result;

  // name parameter가 null 이면 매개변수를 all로 호출, null이 아니면 입력 값 그대로 호출
  if(req.query.name){
    result = await crawlingSvc.getCommodityPrice(req.query.name);
  }else{
    result = await crawlingSvc.getCommodityPrice('all');
  }
  
  console.log(JSON.stringify(result));
  res.send(result);
});

module.exports = router;

 

여기까지 작성 후 터미널 창에서 npm start를 입력하여 서버를 실행하고 아래와 같이 호출하면 API 결과가 정상적으로 리턴되는 것을 확인할 수 있습니다.

  • http://localhost:3000/getPrice

api 호출 결과
all 결과

  • http://localhost:3000/getPrice?name=gas

api 호출 결과2
gas 결과

 

VS Code Docker Extension 설치

여기까지가 그동안 구현했던 Express Rest API + Puppeteer 웹크롤링 서비스를 간략히 정리를 하였고, 본격적으로 이 서비스를 Docker 이미지로 생성하고 실행해보도록 하겠습니다.

먼저, PC에 Docker Desktop(https://hub.docker.com/editions/community/docker-ce-desktop-windows)이 설치가 되어 있어야 합니다.

Docker가 터미널 창에서 커맨드를 통해 동작하지만, 조금이라도 더 쉽게 UI를 통해 사용을 할 수 있도록 VSCode에서 Extension을 제공하고 있습니다. 아래와 같이 Extension탭으로 이동하여 "Docker"라고 검색하여 제일 상단에 나오는 MS에서 제공하는 Docker Extension을 설치합니다.

Docker Extension 설치
Docker Extension

Dockerfile 생성

Docker는 Dockerfile에 작성된 여러 커맨드를 기반으로 Docker 이미지를 생성하게 되는데, 방금 설치한 Extension을 이용하여 쉽게 Dockerfile을 생성하도록 하겠습니다.

VSCode에서 [Ctrl] + [Shift] + [P]를 통해 Command Palette를 실행하여 아래와 같이 "Docker: Add"라고 입력하여 Docker: Add Docker Files to Workspace를 클릭합니다. 그 이후 나오는 설정창은 아래와 같이 선택을 하여 Dockerfile을 생성해줍니다.

Docker add 입력

  • Select Application Platform : Node.js
  • Choose a package.json file : package.json
  • Port : 3000
  • Include optional docker compose files : No

아래와 같이 해당 Workspace에 위에서 입력한 옵션들이 자동으로 커맨드 된 Dockerfile이 생성이 된 것을 확인할 수 있습니다.

도커 파일 생성

 

Puppeteer 공식 가이드(https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#running-on-alpine )에 나와 있는 데로, Docker가 실행되는 경량 리눅스 OS인 alpine에서 기본 설정으로 크로미움이 정상 동작을 하지 않기 때문에, 자동 생성된 Dockerfile에 아래와 같이 Puppeteer관련 RUN와 ENV 커맨드 내용을 추가해줍니다.

FROM node:lts-alpine
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
RUN npm install --production --silent && mv node_modules ../
COPY . .
EXPOSE 3000
# alpine OS에서 크로미움 별도 설치를 하여 Puppeteer 정상 실행
RUN apk add --no-cache \
      chromium \
      nss \
      freetype \
      harfbuzz \
      ca-certificates \
      ttf-freefont \
      nodejs \
      yarn
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
    PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
RUN yarn add puppeteer@10.0.0

RUN chown -R node /usr/src/app
USER node
CMD ["npm", "start"]

 

Docker Image 생성 및 실행

Dockerfile을 기반으로 Docker Image 생성할 때 터미널 창에서 커맨드로 생성을 했어야 하지만, 위에서 설치한 VSCode의 Extension을 통해 아주 간단하게 이미지를 생성하고 컨테이너를 실행할 수 있습니다. 다만 이미지 생성이나 컨테이너 실행 시 세부적인 설정을 할 수 없고 디폴트 설정으로 수행이 되기 때문에, 변경이 필요한 경우 터미널창에서 직접 커맨드로 수행을 해야 합니다.

Docker Image 생성은 위에 작성한 Dockerfile에 마우스 우클릭을 하여 Bulid Image를 클릭하면 자동으로 커맨드가 입력되고 이미지가 생성이 됩니다.

Docker Image 빌드
Docker Image 빌드

생성된 이미지는 좌측 고래 모양의 Docker탭에서 확인이 가능합니다. Docker탭에 중간 IMAGES 항목에 방금 생성한 Docker Image가 있고 그 아래 latest라는 태그 버전이 있습니다. 이 태그 버전에 마우스 오른쪽을 클릭하여 Run을 클릭하면 역시 커맨드가 자동으로 입력되면서 Docker 컨테이너가 실행되는 것을 확인할 수 있습니다.

 

Docker 컨테이너 상태 확인 및 로그 출력

이제 터미널 창에서 해당 컨테이너가 잘 실행되고 있는지 확인하고 로그를 확인해보도록 하겠습니다.

  • docker ps

명령을 통해 해당 컨테이너의 Status를 확인 후, 여기에 표출되는 CONTAINER ID를 이용하여 로그를 조회할 수 있습니다.

  • docker logs -f [컨테이너 ID]

명령을 통해 로그를 실시간 로그를 출력하도록 한 후 http://localhost:3000/getPrice?name=gas과 같이 테스트로 API를 호출하면 아래와 같이 정상적으로 로그가 출력되는 것을 확인할 수 있습니다.

실행 결과

 

 

반응형

댓글