-

Node.js 크롤링(스크래핑)

23 Oct 2019
Node.js 크롤링(스크래핑)

Node.js로 크롤링(스크래핑)하기

2가지 모듈을 이용한다.
axios 모듈과 cheerio 모듈.

두 모듈의 npm 사이트. axios cheerio



axios 모듈의 공식 설명 : Promise based HTTP client for the browser and node.js

간단한 설명 :
axios는 HTTP 클라이언트 라이브러리로써, 비동기 방식으로 HTTP 데이터 요청을 실행한다.
내부적으로 AXIOS는 직접적으로 XMLHttpRequest 를 다루지 않고 “AJAX 호출”을 할 수 있다.

이러한 일을 하므로, axios를 html 문서 긁어오는 용도로 사용 할 것이다.
사실 request 모듈 사용해도 큰 차이는 없을 것 같다.


cheerio 공식 설명 : Fast, flexible & lean implementation of core jQuery designed specifically for the server.

간단한 설명 : HTML 태그에 접근하는데, 서버에서 jQuery의 방식으로 접근 할 수 있도록 해준다.

이번 포스트에서는 데이터 추출해내는 파싱용 모듈로 사용 될 것이다.


1.모듈 설치


npm install --save axios cheerio
모듈 2개 설치해준다.


2.선언


1
2
3
4
5
6
7
    
const axios = require("axios");
const cheerio = require("cheerio");

const url = 'https://store.steampowered.com/search/?specials=1&ignore_preferences=1';
    
    

axios 모듈과 cheerio 모듈 선언하고, url 변수를 생성한다.
이번 포스트에서는 스팀 할인 정보를 긁어올것이다.

3.크롤링


1
2
3
4
5
6
7
8
9
        
const getHtml = async (url) => {
    try {
        return await axios.get(url);
    } catch (error) {
        console.error(error);
    }
};
        

getHtml 변수에 axios.get(url) 함수의 리턴값을 담는다.
동기적 실행을 위해 함수는 async, axios.get 함수는 await으로 선언.

4.파싱


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
            
router.get('/', function(req, res, next) {
    getHtml(url)
        .then(html => {
            let ulList = [];
            const $ = cheerio.load(html.data);
            const $bodyList = $("div#search_resultsRows").children("a");
            $bodyList.each(function(i, elem) {
                const price = $(this).find('div.search_price').text().replace(/,/gi,"")
                    .replace(/ /gi,"").split("");
                ulList[i] = {
                    title: $(this).find('span.title').text(),
                    sale: $(this).find('div.search_discount span').text(),
                    original: price[1],
                    salePrice: price[2],
                    href: $(this).attr('href'),
                    imgSrc: $(this).find('div.col.search_capsule img').attr('src'),
                };
            });
            return ulList.filter(n => n.title);
        })
        .then(result => res.render('index', { title: result }));
});
        
        

결과를 웹으로 보여주기위해 express 프레임워크 같이 사용했다.
getHtml(url)을 동기적으로 실행하고, then()에 다음 할일을 담는다.
ulList 배열 변수 선언하고, $변수에 cheerio를 load한다.

cheerio 내부 함수 설명

  • load : 인자로 html 문자열을 받아 cheerio 객체를 반환합니다.
  • children : 인자로 html selector를 문자열로 받아 cheerio 객체에서 선택된 html 문자열에서 해당하는 모든 태그들의 배열을 반환합니다.
  • each : 인자로 콜백 함수를 받아 태그들의 배열을 순회 하면서 콜백함수를 실행합니다.
  • find : 인자로 html selector 를 문자열로 받아 해당하는 태그를 반환합니다. 그리고 스팀 할인정보의 저 부분을 긁어오고 싶으므로, const $bodyList = $("div#search_resultsRows").children("a") 를 선언.
    이러면 div#search_resultsRows 안에 a태그들이 나뉘어서 배열로 $bodyList 변수에 저장된다.

    이제 원하는 정보들을 cheerio 내부함수 each,find 이용해 가공해서 프론트로 넘겨줬다.

    그 결과.
    간단한 크롤링을 끝냈고, 이제 이 코드를 좀 더 손봐서 할인 전체 정보 긁어오고, DB에 저장할 차례.

    5.전체코드

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
          
    const express = require('express');
    const router = express.Router();
    const axios = require("axios");
    const cheerio = require("cheerio");
    
    const url = 'https://store.steampowered.com/search/?specials=1&ignore_preferences=1';
    
    const getHtml = async (url) => {
        try {
            return await axios.get(url);
        } catch (error) {
            console.error(error);
        }
    };
    
    /* GET home page. */
    router.get('/', function(req, res, next) {
        getHtml(url)
            .then(html => {
                let ulList = [];
                const $ = cheerio.load(html.data);
                const $bodyList = $("div#search_resultsRows").children("a");
                console.log(typeof($bodyList));
                $bodyList.each(function(i, elem) {
                    const price = $(this).find('div.search_price').text().replace(/,/gi,"")
                        .replace(/ /gi,"").split("");
                    ulList[i] = {
                        title: $(this).find('span.title').text(),
                        sale: $(this).find('div.search_discount span').text(),
                        original: price[1],
                        salePrice: price[2],
                        href: $(this).attr('href'),
                        imgSrc: $(this).find('div.col.search_capsule img').attr('src'),
                    };
                });
                return ulList.filter(n => n.title);
            })
            .then(result => res.render('index', { title: result }));
    });
    
    module.exports = router;
    

Published on 23 Oct 2019