XML/RSS 해석
- eXtensible Markup Langauge / Really Simple Syncdication
- XML
- eXtensible Markup Language의 약어로 목적에 맞게 사용될 수 있는 범용적인 데이터 형식
- 기본적으로 텍스트 데이터, 각각의 데이터에 태그를 붙임으로써 문서나 데이터를 구조화 가능
- XML의 목적 : 다른 종류의 시스템 간에 구조화된 문서와 데이터를 쉽게 공유하는 것
- XML은 범용적인 형식이며, XML을 바탕으로 한 다양한 데이터 형식이 존재
- 뉴스 사이트의 요약 정보인 RSS나 벡터 그래픽을 다루는 SVG도 XML을 바탕으로 한다.
- 엑셀/워드 등 마이크로스프트의 오피스 저장 형식도 여러 XML파일을 ZIP으로 압축한 것
- XML은 기계나 인간에게도 다루기 편한 데이터 형식
- XML의 구조
- 기본 구조 : Element(요소)와 Attribute(속성)
- <element_name attribute="value">content</element_name>
- 요소는 내부에 자식 요소를 가질 수 있다.
<?xml version="1.0" encoding="UTF-8"?> <상품 카탈로그> <상품 id="S001"> <상품명>8GB SD카드</상품명> <값>4500원</값> </상품> <상품 id="S002"> <상품명>USB 마우스</상품명> <값>4000원</값> </상품> <상품 id="S003"> <상품명>USB 키보드</상품명> <값>3500원</값> </상품> </상품 카탈로그> |
- XML은 트리 구조로 데이터를 표현할 수 있다.
- XML 데이터 파일에 저장하는 경우에는 XML 선언을 포함할 수 있다.
- Node.js에서 XML을 다루는 법
- Node.js에서는 XML을 파싱하기 위해 'cheerio-httpcli' 모듈을 포함하여 여러 가지 모듈이
존재한다.
- 이 글에서는, xml2js 모듈을 소개
xml2js
- XML 데이터를 자바스크립트 객체로 변환 가능
- 설치 : npm install xml2js
코드
// loading modules
const parseString = require('xml2js').parseString;
// XML data for Testing
const xml = "<fruits shop='AAA'>" +
"<item price='140'>Banana</item>" +
"<item price='200'>Apple</item>" +
"</fruits>";
// Transferring XML
parseString(xml, function(err, result){
// printing result of parsing process
console.log(JSON.stringify(result));
});
결과
- XML이 요소, 내용, 속성의 구조로 되어 있어 JSON과 일대일 대응이 되지 않기 때문에
- 요소의 내용은 '_'이라는 키의 값으로 대입되며
- 속성은 '$'키의 값으로 대입된다.
- 좀 더 구체적으로 사용해보면
코드
// loading module
const parseString = require('xml2js').parseString
// xml data for testing
const xml = "<fruits shop='AAA'>" +
"<item price='140'>Banana</item>" +
"<item price='200'>Apple</item>" +
"</fruits>";
// Transferring xml
parseString(xml, function(err, result){
// name of shop providing fruits
let shop = result.fruits.$.shop;
console.log(`shop= ${shop}`);
// Displaying names and prices of fruits
let items = result.fruits.item;
for(let i in items) {
let item = items[i];
console.log(`--name= ${item._}`);
console.log(` price= ${item.$.price}`);
}
});
결과
- 만약 요소에 자식 요소나 속성이 없을 경우에는 요소 이름에 내용이 바로 대입된다.
코드
// loading modules
const parseString = require('xml2js').parseString;
// XML data for Testing
const xml = "<item>Banna</item>"
// Transferring data
parseString(xml, function(err, result) {
console.log(result.item); // result : Banana
});
결과
- 위 결과 그림과 같이 XML 데이터이지만, '_', '$'를 포함하지 않는 데이터로 변환된다.
- 이런 XML 데이터와 같은 경우 XML과 JS가 거의 1:1로 변환된다.
코드
// loading modules
const parseString = require('xml2js').parseString;
// xml data for Testing
const xml =
`
<items>
<item><name>Banna</name><price>130</price></item>
<item><name>Apple</name><price>300</price></item>
<item><name>Pear</name><price>250</price></item>
</items>
`;
// transferring xml
parseString(xml, function(err, r){
console.log(JSON.stringify(r));
// display each elements
console.log(`----
${r.items.item[0].name[0]}
${r.items.item[0].price[0]}
`);
});
결과
- 만약, JS 객체로부터 XML을 작성하는 경우에는, Builder 클래스를 사용한다.
코드
// loading module
const xml2js = require('xml2js');
// javascript object
const obj = {item:{name:"Banana", price:150}};
// transforming js object to xml
const builder = new xml2js.Builder();
let xml = builder.buildObject(obj);
console.log(xml);
결과
- XML 데이터를 JS 객체로 변환하고 다시 XML로 변환하는 프로그램을 작성해보자
코드
// loading module
const xml2js = require('xml2js');
const parseString = xml2js.parseString;
const Builder = xml2js.Builder;
// xml data for testing
const xml =
`<fruits shop='AAA'>
<item price='140'>Banana</item>
<item price='200'>Apple</item>
</fruits>`;
// transforming xml to js object.
parseString(xml, function(err, res){
console.log(JSON.stringify(res));
// re-transforming js object to xml
let xml = new Builder().buildObject(res);
console.log(xml);
});
결과
RSS
- RSS는 뉴스와 블로그 등 각종 웹사이트의 갱신 정보를 요약하여 전송할 때 사용하는 데이터 형식이다.
- 유명한 포맥 형식 몇가지 : RSS1.0, RSS2.0, Atom 등 (모두 XML을 바탕)
- 기상청의 RSS 읽기
- 현재 기상청에서 RSS로 제공하는 정보는 동네의 시간별 일기예보 및 지역별 중/장기 일기예보
- RSS로 제공되므로 쉽게 기상 예보 정보를 획득 가능
- 기상청 기상예보 RSS
- https://www.weather.go.kr/weather/lifenindustry/sevice_rss.jsp
RSS > 인터넷 > 서비스 > 생활과 산업 > 날씨 > 기상청
홈 > 생활과 산업 > 서비스 > 인터넷 > RSS |날씨|생활과 산업|서비스|인터넷|RSS
www.weather.go.kr
- 예를 들어, 서울/경기도의 RSS 데이터를 살펴보면 포맷은 RSS2.0이며 XML로 구조화되어 매우
규칙적으로 데이터가 담겨있는 것을 알 수 있다.
코드
//Meteorological Agency weather forecast RSS
const RSS = "https://www.weather.go.kr/weather/forecast/mid-term-rss3.jsp?stnId=109";
//loading module
const parseString = require('xml2js').parseString;
const request = require('request');
//downloading RSS
request(RSS, function(err, response, body){
if(!err && response.statusCode == 200) {
analyzeRSS(body);
}
});
// Analyzing RSS
function analyzeRSS(xml) {
// transforming xml to JS object.
parseString(xml, function(err,obj) {
if(err) {console.log(`err=${err}`); return;}
// displaying weather forecast info
let datas = obj.rss.channel[0].item[0].description[0].body[0].location[0].data;
let city = obj.rss.channel[0].item[0].description[0].body[0].location[0].city;
for(let i in datas) {
let data = datas[i];
console.log(`city ${data.tmEf} ${data.wf} ${data.tmn} ${data.tmx}`);
}
});
}
결과
- RSS '/res/channel/item/descrption/body/location' 경로의 데이터를 꺼내서 표시한 결과이다.
- 단계별로 프로그램을 분석해보자면
- RSS를 웹에서 취득한다
- request 모듈로 RSS를 요청하고, 콜백 함수 인자로 넘어온 statusCode가 정상일 때 가동
- 데이터를 취득 후, analayzeRSS() 메소드를 호출
- parseString() 메소드를 사용하여 XML 데이터를 자바스크립트 객체로 변환
- 변환한 날씨 정보를 for 문으로 차례차례 콘솔에 출력
- XML/RSS 파싱에 'cheerio-httplci'를 사용하는 방법
코드
//Meteorological Agency weather forecast RSS for Node.js using cheerio
// weather RSS
const RSS = "https://www.weather.go.kr/weather/forecast/mid-term-rss3.jsp?stnId=109";
// loading module
const client =require('cheerio-httpcli');
// downloading RSS
client.fetch(RSS, {}, function(err, $, res){
if(err) {console.log(`err : ${err}`); return;}
// display needed element by extracting
let city = $("location:nth-child(1) > city").text();
$("location:nth-child(1) > data").each(function(idx) {
let tmEf = $(this).find('tmEf').text();
let wf = $(this).find('wf').text();
let tmn = $(this).find('tmn').text();
let tmx = $(this).find('tmx').text();
console.log(`${city} ${tmEf} ${wf} ${tmn}~${tmx}`);
})
});
결과
- RSS 전체 경로를 지정하지 않고, 'location:nth-child(1) > city' 처럼 CSS 선택자를 지정해주고 있다.
- 'cheerio-httpcli'를 사용하면 CSS 쿼리를 사용하여 원하는 부분의 정보를 쉽게 추출 할 수 있어
더욱 편리하기 프로그램을 작성 가능하다.
정기적으로 다운로드
- 정기적인 처리를 수행
- 정기적으로 처리를 실행하는 스케줄러
- Mac OS X나 리눅스에는 cron이라는 데몬 프로세스가 존재
- cron을 사용하면 스크립트를 원하는 시점에 자동으로 실행할 수 있다.
- 비슷하게 윈도우에도 작업 스케줄러라는 것이 존재한다.
- 정기적인 실행을 위한 힌트
- cron을 이용하여 정기적으로 수행하면 좋은 작업들
- 데이터 수집과 같은 애플리케이션의 정기적인 처리 (데이터베이스 집계 등)
- 로그, 백업 등 시스템과 관련된 정기적인 처리 (가벼운 시스템을 위한 로그 파일 옮기기 등)
- 시스템이 제대로 동작하고 있는지 정기적으로 감시하는 처리 (시스템 장애시, 관리자 메일 통지 등)
- 환율의 변동을 확인하는 API 사용
- '환율 확인 API' : 저자가 작성하고 제공하는 웹 API (데이터 갱신 빈도가 낮다)
- JSON 형식의 활용 : http://api.aoikujira.com/kawase/get.php?code=USD&format=json
- XML 형식의 활용 : http://api.aoikujirda.com/kawase/get.php?code=USD&format=xml
- API를 호출하면 미국 달러(USD)를 기반으로 한 값이 반환된다.
코드
//getting exchange rate info for Node.js
// Exchange Rate info API
const API = "http://api.aoikujira.com/kawase/get.php?code=USD&format=json";
// loading modules
const request = require('request');
const fs = require('fs');
// Requesting web API
request(API, function(err, response, body){
// checking http err
if(err || response.statusCode != 200) {
console.log(`ERROR ${err}`); return;
}
// Transforming JSON to JS object
let res = JSON.parse(body);
let krw = res["KRW"];
// saving exchange rate info to file(displaying data at a filename)
let time = new Date();
let fname =
`USD_KRW_${time.getFullYear()}-${time.getMonth()+1}-${time.getDate()}.txt`;
let text = `1usd=${krw}krw`;
console.log(text);
console.log(fname);
fs.writeFileSync(fname, text);
});
결과
- 리눅스/Mac OS X의 경우
- Linux 계열은 대부분 cron이 표준으로 설치되어 있다.
- cron을 사용하려면 지정된 설정 파일에 지정된 형식으로 실행 간격을 기술하면 된다.
- 텍스트 파일에 설정을 기술하는 UNIX 전통을 따르고 있다.
- cron을 설정하려면 터미널에서 crontab이라는 명령을 수행하여 편집한다.
- crontab -e
- cron 설정 화면이 열린다. (처음에는 아무런 스케줄이 없는 상태일 것이다)
- 작업 디렉토리에 주의
- cron이 수행될 때의 작업 디렉토리는 사용자의 홈 디렉토리가 된다. 그래서 로그를 저장하거나
할 때에는 전체 경로를 저장하거나 작업 디렉토리를 변경해주어야 한다.
* 아쉽게도 GoormIDE 내에서는 corn이 동작하지 않았다.
로그인이 필요한 웹 사이트 크롤링
- PhantomJS와 CasperJS
- 여러 페이지를 이동하거나 로그인 후의 데이터를 취득할 때의 전용 도구
- PhantomJS : 화면이 없는 브라우저 (커맨드라인 방식)
- CasperJS : PhantomJS를 쉽게 사용하기 위한 라이브러리
- PhantomJS
- 커맨드라인에서 쓸 수 있는 웹 브라우저
- 렌더링 엔진으로 WebKit을 채용
- 커맨드 랑니으로 브라우저를 조작하고, 브라우저 안의 데이터를 취득하거나 스크린샷을 찍을 수
있다.
- 웹 사이트에서 데이터를 스크래핑하거나 UI 테스트 자동화 등에 활용된다.
- CapserJS
- PhantomJS를 쉽게 사용하기 위한 라이브러리
- 설치
- 설치하기전 Ubuntu 18.04에서 미리 설치할 것 : sudo apt-get install libfontconfig
- PhantomJS 설치 : npm install -g phantomjs
- CasperJS 설치 : npm install -g casperjs
- PhantomJS와 CasperJS를 사용해 웹 사이트의 타이틀을 표시하는 프로그램
코드
// displaying title of websites
const TARGET_URL = 'http://jpub.tistory.com';
// create CasperJS object
const casper = require('casper').create();
// open the websites
casper.start(TARGET_URL, function() {
// priniting a title
this.echo(casper.getTitle());
});
// executing process
casper.run();
결과
- 'jpub.tistory.com'에 접속하여 페이지의 타이틀을 획득하고 콘솔에 출력한다.
- CasperJS 객체를 생성하고, 방문할 페이지 URL를 start() 메소드의 인자로 지정한다.
- start()의 두번 째 인자로, 페이지가 로드되었을 때 수행되어야할 메소드를 지정한다.
- 페이지가 로드된 후 실행되는 처리는 : 현재 접속한 사이트의 제목을 취득하고 출력하는 것
- CapserJS의 런타임정보에 관해 더 자세히 알고 싶은 경우 실행 시 '-verbose'나 '-log-level=debug'
를 인자로 주면 자세한 디버깅 정보가 색상과 함께 표시된다.
- 화면 캡처 프로그램
코드
var casper = require("casper").create();
casper.start();
casper.open("https://devel-up-tree.tistory.com");
casper.then(function(){
casper.capture("screenshot2.png");
});
casper.run();
결과
- 플리커 이미지 검색 결과 캡처하기
코드
// Capturing result of resarching on flick for CasperJS
// Creating CasperJS obj
const casper = require('casper').create();
// executing CasperJS process
casper.start()
// config of viewport
casper.viewport(1400,800);
// config of UserAgent
casper.userAgent('User-Agent: Mozila/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36');
// researching word 'cat' on flickr
const text2 = encodeURIComponent('고양이');
casper.open('https://www.flickr.com/search/?text='+text2);
// capturing view
casper.then(function(){
this.capture('flickr-cat.png', {
top:0, left:0, width:1400, height:800
});
});
// run
casper.run();
결과
- 위 코드처럼 CasperJS는 start() 메소드와, run()메소드 사이에 순서대로 실행하고자 하는 처리를
then() 메소드를 사용하여 지정하면 된다.
- CasperJS도 비동기 처리가 기본이지만, then() 메소드를 사용하면 then() 메소드 안에 정의한
내용의 수행이 완료되기 전에 그 다음 then() 메소드로 넘어가지 않으므로 동기적인 함수 수행을
쉽게 기술할 수 있다.
- User-Agent란 웹사이트에 접속할 때 사용하는 프로그램을 말한다.
- 이 사용자 에이전트에 값을 설정함으로써 모바일 전용 페이지로 접속할 수 있다.
- 아이폰 인척하기
casper.userAgent(‘Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X)
AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53’);
// 화면 사이즈 지정
casper.viewport(750, 1334);