코딩 강의/유용한 스킬 없을까요

자바스크립트 크롤링 따라하기

아미넴 2020. 8. 26.
반응형

안녕하세요.

오늘은 번외로 실용적인 내용을 준비했습니다.

 

목차

     

    크롤링이 필요한 이유

    아끼는 대학 후배를 오랜만에 만났는데 한 웹사이트에서 검색을 하여 수백 건에 이르는 결과를 일일이 한 땀 한 땀 엑셀에 붙여넣는 노가다성 작업을 한다면서 힘들어 하더라구요.

     

    그 말을 듣고 제가 가만히 있을 수는 없었죠.

    어떻게든 한방에 할 수 있는 방법을 찾아보겠다고 호언장담을 했습니다.

     

    크롤링이란

    사실 크롤링이라는 용어는 잘 몰랐는데 들어보니 저는 스크래핑으로 알고 있었던 기술이었어요.

    보통 이 두 가지 용어를 혼용해서 쓰는거 같아요.

     

    간단히 말하면 웹페이지 내용을 그대로 읽어와서 원하는 내용을 추출하는 작업인데요.

    어떻게 사용하느냐에 따라 굉장히 유용한 결과물을 얻어낼 수 있어 보입니다.

     

    크롤링을 위한 사전 준비

    이 분야에서는 보통 파이썬(Python)이 많이 쓰인다고 하네요.

    아무래도 비전공자들이 사용하기 쉽고 제공하는 라이브러리도 많은 언어라서 그런거 같아요.

     

    그런데 저는 파이썬을 다뤄 본 적이 많지 않아서 JavaScript를 이용하여 작업을 했습니다.

     

    조만간 파이썬으로도 하는 방법을 정리해서 게시하도록 해볼게요. :)

     

    +내용 추가 (2020.09.01)

    파이썬 크롤링 모음 이동

    약속 지켰습니다 ㅎㅎ

    이 내용은 관심있는 분만 보시면 됩니다.

     

    nodeJS 및 필요 모듈 설치

    먼저 nodeJS가 필요합니다.

    설치 방법을 잘 모르시겠다면

    링크를 하나 남겨 놓을테니 참고하시면 되구요.

    node js 설치 (on Windows)

     

    node js 설치 (on Windows)

    간단하게 html, css, javascript를 올려볼 node 서버가 필요했습니다. Windows에 빠르게 설치해 보겠습니다. 아래 node js 페이지에서 Windows 설치 파일(msi)을 다운 받습니다. https://nodejs.org/en/download/..

    dejavuqa.tistory.com

    서버까지 띄우실 필요는 없어요.

     

    그 다음으로 axios, cheerio 라는 두 가지 모듈은 설치하셔야 됩니다.

    터미널 작업할 디렉토리에서 npm 명령어를 이용하여 다음과 같이 입력하세요.

    npm i axios cheerio

    axios는 URL을 호출하여 웹페이지 HTML을 그대로 가져와 Promise 형태로 반환하는 모듈입니다.

    자세한 사항은 아래 참고하시면 되구요.

    axios/axios

     

    axios/axios

    Promise based HTTP client for the browser and node.js - axios/axios

    github.com

    cheerio는 HTML DOM 형태의 데이터에서 원하는 내용을 쉽게 추출할 수 있게 해주는 기능을 가진 모듈이에요.

    더 궁금하신 사항은 아래 링크에서 확인하시면 됩니다.

    cheerio

     

    cheerio

    Fast, flexible, and lean implementation of core jQuery designed specifically for the server.

    cheerio.js.org

     

     

    크롤링 코드 작성

    이제 코드를 작성해 보겠습니다.

    먼저 위에서 설치했던 모듈을 포함하여 아래와 같이 세 가지 모듈을 불러옵니다.

    const axios = require("axios");
    const cheerio = require("cheerio");
    const fs = require('fs');

    fs는 파일시스템 모듈로 nodeJS 기본 제공 모듈입니다.

    텍스트 파일을 읽고 쓰기 위해 사용합니다.

     

    그 다음 필요 전역 변수를 선언해줍니다.

    var resultList = [];
    var cnt = 0;

    결과 데이터를 저장할 배열 변수 및 마지막에 파일로 쓰기 위해 카운팅 변수를 하나 선언했습니다.

     

    다음으로 URL을 호출하여 HTML 데이터를 받아오는 함수 하나를 정의합니다.

    function delay(ms) {
        return new Promise(function(resolve, reject) {
            setTimeout(function(){
                resolve();
            },ms);
        });
    }
    
    function getHTML(url) {
        return new Promise(resolve=>{
            delay(300).then(function() {
                axios.get(url).then(function(data) {
                    resolve(data);
                });
            });
        })    
    }

    axios 모듈을 이용하여 입력받은 URL을 호출하여 HTML 데이터를 그대로 받아오는 기능을 구현했는데요.

    여러 건 호출할 경우를 대비하여 0.3초 간격으로 호출되도록 구현하였습니다.

     

    그리고 실습해 볼 샘플 파일을 하나 가져왔습니다.

    sample.txt
    0.00MB

     

    아래와 같이 크롤링에 필요한 URL 정보가 다수 담겨 있습니다.

    이제 본격적으로 메인 함수를 구현하겠습니다.

    function main() {
        fs.readFile('sample.txt','utf8',function(err, data){
            var allText = data;
            var list = allText.split('\n');
            var workList = [];
            for(var i=1; i<list.length-1;i++){
                workList.push(list[i].split('^')[4]);
            }
            makeJSON(workList); // 아직 미구현
        });
    }
    
    main();

    fs 모듈을 이용하여 같은 경로에 있는 sample.txt 파일을 읽어옵니다.

     

    먼저 읽어온 내용을 개행문자(\n) 기준으로 나누어 리스트로 만들었습니다.

    그리고 다시 각각을 구분기호(^) 기준으로 나누어 마지막에 있는 URL 정보만 workList 변수에 배열로 저장하였습니다.

    그리고 makeJSON 함수를 만들어서 제귀 호출을 할 예정입니다.

     

    정제한 URL 정보로 앞서 정의한 함수를 호출하여 HTML 데이터를 가져와 보겠습니다.

    function makeJSON(workList) {
        getHTML(workList[cnt]).then(html => {
            let result = {};
            const $ = cheerio.load(html.data);
            result['title'] = $("body").find(".search_tit").text();
            result['date'] = $("body").find(".tit_loc").text();
            result['content_trans'] = $("body").find(".ins_view_pd").find(".paragraph").eq(0).text();
            result['content_origin'] = $("body").find(".ins_view_pd").find(".paragraph").eq(1).text();
            return result;
        })
    }

    먼저 workList 배열을 인자로 받는 makeJSON 함수를 만듭니다.

    사이트에 직접 들어가 HTML 소스를 분석해 본 결과 제목은 search_tit, 날짜는 tit_loc, 본문 내용은 ins_view_pd 라는 class를 이용하고 있는 것을 확인할 수 있었습니다. 본문은 다시 번역문과 원문으로 나뉘는데 서로 같은 클래스를 이용하고 있어서 인덱스로 구분하였습니다. 그리고 그 안에서 실제 필요한 내용은 다시 paragraph 이름의 class를 가지고 있는 부분이었습니다. 그에 따라 cheerio 모듈을 이용하여 데이터를 분리하였고 JSON 형태로 값을 담았습니다.

     

     

    이제 결과를 파일로 저장하는 일만 남았습니다.

    function makeJSON(workList) {
        getHTML(workList[cnt]).then(html => {
            let result = {};
            const $ = cheerio.load(html.data);
            result['title'] = $("body").find(".search_tit").text();
            result['date'] = $("body").find(".tit_loc").text();
            result['content_trans'] = $("body").find(".ins_view_pd").find(".paragraph").eq(0).text();
            result['content_origin'] = $("body").find(".ins_view_pd").find(".paragraph").eq(1).text();
            return result;
        })
        // 추가 작성
        .then(res => {
            cnt++;
            resultList.push(res);
            if(workList.length == cnt){
                fs.writeFile('result_json.txt', JSON.stringify(resultList), 'utf8', function(error){
                    console.log('write end');
                });
            } else {
                makeJSON(workList);
            }
            console.log(cnt);
        });
    }

    호출할 URL이 여러 건인 경우 0.3초 간격으로 제귀 호출이 됩니다.

    cnt 변수를 하나씩 증가하면서 작업을 하며 마지막 결과를 받았을 경우 모든 결과를 한 번에 파일로 저장을 하고 작업이 종료됩니다. 이 경우에도 파일 저장에 fs 모듈이 사용되었습니다.

     

    전체 소스

    최종 점검을 위해 전체 소스를 제공합니다.

    const axios = require("axios");
    const cheerio = require("cheerio");
    const fs = require('fs');
    
    var resultList = [];
    var cnt = 0;
    
    function delay(ms) {
        return new Promise(function(resolve, reject) {
            setTimeout(function(){
                resolve();
            },ms);
        });
    }
    
    function getHTML(url) {
        return new Promise(resolve=>{
            delay(300).then(function() {
                axios.get(url).then(function(data) {
                    resolve(data);
                });
            });
        })    
    }
    
    function makeJSON(workList) {
        getHTML(workList[cnt]).then(html => {
            let result = {};
            const $ = cheerio.load(html.data);
            result['title'] = $("body").find(".search_tit").text();
            result['date'] = $("body").find(".tit_loc").text();
            result['content_trans'] = $("body").find(".ins_view_pd").find(".paragraph").eq(0).text();
            result['content_origin'] = $("body").find(".ins_view_pd").find(".paragraph").eq(1).text();
            return result;
        })
        // 추가 작성
        .then(res => {
            cnt++;
            resultList.push(res);
            if(workList.length == cnt){
                fs.writeFile('result_json.txt', JSON.stringify(resultList), 'utf8', function(error){
                    console.log('write end');
                });
            } else {
                makeJSON(workList);
            }
            console.log(cnt);
        });
    }
    
    function main() {
        fs.readFile('sample.txt','utf8',function(err, data){
            var allText = data;
            var list = allText.split('\n');
            var workList = [];
            for(var i=1; i<list.length-1;i++){
                workList.push(list[i].split('^')[4]);
            }
            makeJSON(workList);
        });
    }
    
    main();

     

    작성한 내용은 crawling.js로 저장하겠습니다.

    그리고 터미널에서 아래와 같이 실행해 줍니다.

    node crawling.js

     

    결과 확인

    작업이 완료되면 같은 경로에 result_json.txt 파일이 생성된 것을 확인할 수 있습니다.

     

    생성된 파일을 메모장으로 열어보니

    이와 같은 형태로 데이터가 잘 저장된 것을 볼 수 있었습니다.

     

    이처럼 JSON 형태로 저장을 하면 원하는대로 가공하여 여러 방면에 활용할 수 있어보입니다.

    그럼 다음에는 파이썬으로 크롤링 하는 방법도 알아보도록 하겠습니다.

     

    감사합니다.

     

     

    ↓↓파이썬을 활용한 크롤링 모음↓↓

     

    파이썬 텍스트 크롤링 따라하기

     

    파이썬 텍스트 크롤링 따라하기

    #함께보면_좋은강의 파이썬 크롤링을 활용하여 캔버스 이미지 저장하기 파이썬 크롤링을 활용하여 캔버스 이미지 저장하기 #함께보면_좋은강의 sangminem.tistory.com/83 Python(파이썬) 크롤링(Crawling)

    sangminem.tistory.com

    파이썬 크롤링을 이용하여 인스타그램 맞팔 여부 자동 확인하기

     

    파이썬 크롤링을 이용하여 인스타그램 맞팔 여부 자동 확인하기

    안녕하세요. 저도 크롤링을 처음 공부하면서 여러가지를 해보는 중인데요. 이번에는 최근 인기있는 SNS 중 하나인 인스타그램에 적용해 볼까 합니다. 흥미를 주기 위해 사람의 심리를 조금 이용

    sangminem.tistory.com

    파이썬 크롤링을 활용하여 캔버스 이미지 저장하기

     

    파이썬 크롤링을 활용하여 캔버스 이미지 저장하기

    #함께보면_좋은강의 sangminem.tistory.com/83 파이썬 크롤링으로 URL 이미지 저장하기 #함께보면_좋은강의 sangminem.tistory.com/30 파이썬 크롤링을 활용하여 캔버스 이미지 저장하기 #함께보면_좋은강의 san

    sangminem.tistory.com

    파이썬 크롤링으로 URL 이미지 저장하기

     

    파이썬 크롤링으로 URL 이미지 저장하기

    #함께보면_좋은강의 sangminem.tistory.com/30 파이썬 크롤링을 활용하여 캔버스 이미지 저장하기 #함께보면_좋은강의 sangminem.tistory.com/83 Python(파이썬) 크롤링(Crawling)으로 URL 이미지 저장하기 #함께..

    sangminem.tistory.com

     

    반응형
    그리드형(광고전용)

    댓글4

    • LUIBICTO 2020.12.27 01:17

      ypeError: Cannot read property 'split' of undefined
      at ReadFileContext.callback (C:\Users\eodnj\OneDrive\바탕 화면\webcrawling\public\script.js:62:28)
      at FSReqCallback.readFileAfterOpen [as oncomplete] (fs.js:273:13)

      cmd 창에 이런 문구가 뜨는데 split 자체를 쓰는건 문제 없어 보이는데 무슨 문제인지 알 수 있을 까요?
      답글

      • 아미넴 2020.12.27 10:18 신고

        안녕하세요!

        원인을 정확하게 파악하기 위해서는 스플릿을 쓰는 부분에서 디버깅을 해보거나 console 창에 해당 변수를 출력해 봐야 할 것 같은데요.

        어떻게 하셨는지 정보가 부족하여 정확히 알 수는 없지만 추측하자면 배열 인덱스가 잘못됐을 것으로 판단됩니다.

        예를 들어 위의 코드 같은 경우 스플릿 메서드를 두 번 활용하였는데 최초 스플릿 메서드로 만들어진 배열 원소 개수가 10개라고 한다면 두번째 스플릿 메서드에서 배열 인덱스를 10 이상 참조하려고 한 경우 문제가 발생할 수 있을 것 같습니다.

        더 궁금하신 점 있으시면 말씀 부탁드릴게요~

    • 노력중.. 2021.04.14 09:29

      크롤링 해보고싶어서 열심히 따라 쳐보고, 안되서 아예 복사붙여넣기까지 해봤는데..
      왜 저는 800A03EA 이 구문오류가 나오는걸까요..?
      답글

    💲 추천 글