(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);
}
])
웹 크롤링 -> 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);
}
])
댓글
댓글 쓰기