기본 콘텐츠로 건너뛰기

(Node.js) XLSX로 결과 출력하기 / 모듈 디자인 Exporting / Node.js modular design

Node.js에서 XLSX 라이브러리를 제공하기 때문에,

웹 크롤링 -> XLSX Export -> 주기적 메일링
하는 방식으로 데이터 수집이 가능할 것 같다고 생각했습니다.

그래서 이번에는
1) 전에 만들었던 웹크롤러 모듈화
2) XLSX 라이브러리를 사용해서 외부로 Export
3) 모듈화한 것을 외부에서 호출

요래 3가지를 한 번 해봤습니다!

전 근본없는 node.js 코딩을 하기 때문에 먼저 원문 내용을 읽으시는 것이 좋겠습니다.
https://github.com/SheetJS/js-xlsx

이제 시작입니다.

-----
기본적으로 아래와 같이 XLSX 라이브러리를 불러오고 , 내장 wb를 하나 만들어야 합니다. 가상의 Workbook 같은걸로 이해해주셔요.

// ----- XLSX Initialization part ----- //

const XLSX = require('xlsx');
/* Initializing a free workbook & worksheet name*/
let wb = XLSX.utils.book_new();

// ----- End of XLSX Initialization part ----- //




-----
그 다음 let ws_data =[]; 빈 Array를 선언해버립시다.

그러면, Title과 url을 URL이 undefined가 아닐 때만 dynamic allocation하는 concept으로 아래와 같이 설정합니다.
새 행마다 new로 dynamic allocation을 하고, push로 열에다가 밀어넣는다! 는 concept입니다.
엑셀에서 행렬로 그려보면 대충 저런 모습일 것입니다.

                        열        열       열
행 new array -> push() push() push()
행 new array -> push() push() push()
행 new array -> push() push() push()
행 new array -> push() push() push()
행 new array -> push() push() push()



-----
여기서부터는 XLSX로 Export하는 부분입니다.

1) aoa_to_sheet 는 위에서 누적한 데이터를 worksheet로 바꿔줍니다. (아마도 메모리에서요)
2) book_append_sheet worksheet를 workbook에다가 추가 해줍니다. (아마도 메모리에서요??)
3) writeFile을 거치면 드디어 파일이 하나 생성됩니다. 감동!
4) 사용이 끝난 array를 비워줍니다.

이 부분은 Trial & Error + Github 선생님들 말씀에 예제 따라해서 해보니까 되는 코드를 써놨습니다.


-----
이 부분은 위의 Function을 Export해서 쓸 수 있게하는 부분입니다.
Module.Export = (함수 이름)을 하면 외부에서 호출할 수 있습니다.



만약에 Module.Export.(어떤 이름) = (함수 이름)
요래 하면 저 어떤 이름을 사용할 수 있게 됩니다.

예로 Module.Export.BigAndBeautifulHomi = HomiOfShims

이렇게 쓴다면(File이 ./test.js에 있다고 칩시다.), 외부에서는
let homi = require('./test') // 외부 모듈 import할 때는 .js를 빼야하네요!

homi.BigAndBeautifulHomi()
이렇게 호출해야 하게 됩니다. 모듈에 여러가지 함수가 있다면 각 함수마다 이름을 이쁘게 붙여줘야 하겠쥬~

-------
이제는 외부에서 호출하는 단계입니다.
TestField를 밑과 같이 적고, 해당 함수에 변수를 넣고 돌려봅니다.

불행히도 제가 Node.js 입문자라서 저 변수를 배열로 전달하는 방법을 아직 모릅니다.. ㅠㅠ 오늘 3시간은 찾아본 것 같은데 능력부족입니다. 이게 다 ㅠㅠ



잘 출력되는 것을 확인했습니다.


제가 참 좋아하는 XLSX 파일이 아름답게 정리되어서 기분이 좋습니다.

그럼 다음 단계에서는 메일링 시스템을 구축해서 콤퓨타만 켜놓으면 주기적으로 데이터를 수집해서 메일로 쏴주는 모듈과 함께 조립해보려고 합니다.


-----

(여기서부터는 헛소리입니다.)
1. 제가 전자공학 학사에 회사에서 주업무는 물리학 쪽이고, 보조 업무에 초~중급 레벨의 VBA,C++,Python을 주로 쓰고 있는데요,
대량 데이터를 거의 100% 인덱스 기준으로 처리하다보니까 저런 FIFO가 왜 필요한지 처음 배우고 12년 동안 전혀 이해를 못했습니다.
근데 오늘 push를 써보니까 굳이 인덱싱을 안해도 되고 여튼 좋은 함수인거 같습니다.(특히 인덱싱하다가 조건문 헷갈리면 가끔 등골 싸합니다;;)
아마 오늘 이후부터 제가 만든 모듈에는 저런 pop-like 테크닉이 들어간 걸 많이 쓸 거 같습니다.

2. 사내망 데이터 수집 후 관련 인원들에게 뿌려주려고 배우기 시작한 언어인데 Node.js가 파이썬이랑 유사하게 생산성이 엄청 좋은 것 같습니다.
저같이 저레벨 유저도 Ctrl C+V로 엑셀 파일 하나 쓰는게 가능하다니요~ 타이빵라!


----- Crawler module -----
/*

  TestPjt Title : Shims TestCrawler with xlsx writing module
 
  Use VSCode to edit script code of this program for this Node.js Module
  First written on August 2nd
*/

/*
  Pjt modified to General Purpose WebCrawling & XLSX file exporter
  Modifed on August 3rd

*/

const cheerio = require('cheerio');
const request = require('request');
const Iconv = require('iconv-lite');
const log = console.log;

// ----- XLSX Initialization part ----- //

const XLSX = require('xlsx');
/* Initializing a free workbook & worksheet name*/
let wb = XLSX.utils.book_new();

// ----- End of XLSX Initialization part ----- //

// Module Export Part
module.exports = homiOfShims;

// ----- Start of function description ----- //
function homiOfShims(homiOptions){
  // --- GodKimchi's Advice(190804) - make options as set of variables
  // so it would be more good to see at user-input part
let {
targetSite,
targetEncoding,
H_body,
H_parents,
H_children,
H_exportWb,
H_exportWs,
H_titleTarget,
H_titleAttr,
} = homiOptions;
  // --- end of advice point

  if (targetEncoding == 'EUC-KR') {
    targetEncoding = null
  }
  const requestOptions  = { method: 'GET'
    ,uri: targetSite
    ,headers: {"User-Agent": "Mozilla/5.0"}
    ,encoding: null
  };
       
  /* make worksheet */

  function requestCallback(error, response, H_body){
    let ws_data = [];
    let count = 0;
    if (!error) {
      // Data extractor when encoding == EUC-KR
      // GodKimchi's Advice(190804) - Using Three variable operator to reduce entire code
      const $ = cheerio.load(targetEncoding == null ? Iconv.decode(H_body, 'EUC-KR') : H_body);
      // Crawling part
      const $bodyList = $(H_parents).children(H_children);
      $bodyList.each(function(i, elem) {
      let titleText = $(this).find(H_titleTarget).text();
      let titleUrl = $(this).find(H_titleAttr).attr('href')
        if(titleUrl != undefined) {
          log('title : ' + titleText);
          log('url : ' + titleUrl);
          ws_data[count] = new Array();
          ws_data[count].push(titleText);
          ws_data[count].push(titleUrl);
          count = count + 1
        }
      });
    };
  /* Add the worksheet to the workbook */
  let ws = XLSX.utils.aoa_to_sheet(ws_data);
  XLSX.utils.book_append_sheet(wb, ws, H_exportWs);
  /* Write workbook into XLSX File*/
  XLSX.writeFile(wb, H_exportWb);
  ws_data =[];
  }

  request(requestOptions, requestCallback);
};


----- Execute module -----
let homi = require('./Shims_ReqCrawII')
let async = require('async')

async.waterfall([
    function(callback){
        let homiOptions = {
            targetSite : 'https://news.naver.com/main/list.nhn?mode=LSD&mid=sec&sid1=001',
            targetEncoding : 'EUC-KR',
            H_body : 'body',
            H_parents : '#main_content > div.list_body.newsflash_body > ul.type06_headline',
            H_children : 'li',
            H_exportWb : 'YouveGotHomi-ed.xlsx',
            H_exportWs : 'HomiedPage1',
            H_titleTarget : 'dl > dt > a',
            H_titleAttr : 'dl > dt > a'
        }
        // --- GodKimchi's Advice(190804) - make options as set of variables
        // Variable set would be transferred to module as a set, and seperated in module side.
homi(homiOptions);
        // --- end of advice point
        callback(null);
    },
    function(){
    let homiOptions = {
        targetSite : 'http://gamewoori.com/board/board.html?code=gamewoori_board9',
        targetEncoding : 'EUC-KR',
        H_body : 'tbody',
        H_parents : 'div.bbs-table-list > table > tbody > tr',
        H_children : 'td',
        H_exportWb : 'YouveGotHomi-ed.xlsx',
        H_exportWs : 'HomiedPage2',
        H_titleTarget : 'div.tb-left > a',
        H_titleAttr : 'div.tb-left > a'
        }
homi(homiOptions);
    }
])

댓글

이 블로그의 인기 게시물

(VBA) 009 - 닫힌 파일에서 데이터 읽어오기 (ExecuteExcel4Macro)

#毎日育ちゃん可愛い大会 예시의 매크로 파일을 테스트 할 때는 저장된 폴더를 사용하실 폴더로 꼭 바꿔주세요! (pptx파일) pptx파일 (xlsx파일) 예제데이터파일   Macro파일 ★ 진행목적 : 왜 이걸 사용합니까 . 1) 행이나 열 , 또는 Sheet 과 같이 다른 특성을 가지는 1,2,3 차 데이터배열에 대한 처리 방법을 지금까지 설명드렸습니다 . 2) 그럼 이제 , 다른 파일에서 데이터를 읽어올 방법을 알아볼 필요가 있습니다 . 어째선가 회사의 데이터를 처리하다보면 , 주기적인 이름의 엑셀 파일 특정 Sheet, Cell 에 있는 경우가 많았습니다 . 3) 엑셀에서 이미 열려있는 파일의 참조는 ‘=‘ 을 사용하면 가능하지만 , 닫힌 파일은 데이터를 읽지 못합니다 . 4) 그래서 이를 처리하기 위해 VBA 의 ‘ExecuteExcel4Macro( 주소 )’ 를 사용합니다 ! ★ 다른 파일의 참조는 어떻게 합니까 ? 1) 열려 있는 다른 파일의 데이터를 읽는 방식은 ‘=‘ 을 입력하고 해당 Cell 을 클릭하면 됩니다 ! 2) 그러면 아래와 같이 (=‘ 파일이 있는 폴더 [ 파일명 ]Sheet 명 ’!Cell 주소 ) 의 형태로 기록 이 됩니다 . ★ 닫힌 파일에 대해서는 INDIRECT 는 사용이 되질 않습니다 ! 1) INDIRECT 로는 처리가 되질 않습니다 . 2) 어째선가 전에 사용하던 INDIRECT 를 사용하고 싶지만 , 사용이 되질 않습니다 . 검색을 해봐도 안된다는 답변만 있네요 ! 3) 파일이 하나 두 개라면 , 이전과 같이 ‘=‘ 를 쓰면 되겠지만 , 그러면 자동화를 통한 효율화가 불가능해지겠죠 ! 4) 그래서 이를 처리하기 위해 VBA 의 ‘ExecuteExcel4Macro( 주소 )’ 를 사용합니다 ! ★ ExecuteExcel4Macro 는 어떻게 사용합니까 ? 1) VBA 의 ExecuteExcel4Macro 란 매크로...

[Python] 크롤링 HTTP Forbidden 403 문제 해결

  코드에는 문제가 없는데 HTTP 403 Forbidden이 발생하길래.. 검색해보니까 서버사이드에서 저 같은 불법 크롤러를 거르는 메쏘드가 있는 모양입니다. 아래의 링크를 참고해서 해결했습니다 https://howtoworld.tistory.com/52 urllib에서 Request를 import 해줘야합니다. 1 2 3 4 def  hellenicshipping():     url  =  Request( "https://www.hellenicshippingnews.com/category/shipping-news/dry-bulk-market" ,headers = { 'User-Agent' :  'Mozilla/5.0' })     html  =  urlopen(url)     savefile  =   './hsn.txt' Colored by Color Scripter cs

(VBA) 004 - Object 이름으로 이미지 복사 붙여넣기

(VBA) 004- Object 이름으로 이미지 복사 붙여넣기 진행목적  :  왜 이걸 사용합니까 . → Object  이름을 사용해서 주기적 복사가 가능한 경우가 있습니다 .     ( 예  :  사진의 이름이  ‘ 사진  1’ ‘ 사진  2’…  로 되어있거나 , ‘Picture 1’, ‘Picture 2’…  로 됨 ) (설명자료는 여기) 설명자료 ( 예제파일은 여기 ) 예제파일 (VBA 진행에 대해) 이제 VBA 세션 자료를 필요할 때 보려고 이 블로그에 남기려고 해요. 앞선 001-003도 옮겨야겠습니다. 올해 내에 100가지 주제를 가지고 포스팅 할 수 있도록 할게요. ( 中谷 育 さんのイメージで に対して) 本当に可愛い中谷 育さんのイメージが 含まれています。 ありがとうございます。 何か問題があったら、教えてください。 直ちに処理します。