[개발] Iconv를 이용한 EUC-KR 문서 파싱하기

간혹 우리는 특정 웹 페이지에 대한 정보를 가져오기 위한 스크래퍼를(Scraper) 만들어야 하는 경우가 있다. 하지만 아직까지 많은 수의 국내 웹 사이트가 EUC-KR 인코딩을 사용하고 있어서 UTF-8을 기본으로 하는 스크래퍼 환경에서는 알 수 없는 문자열이 출력되거나 페이지의 정보를 제대로 가져올 수 없는 현상이 일어난다.

이런 문제는 당연히 과거에서부터 문제가 있었고 이를 해결하기 위해 Iconv라는 라이브러리가 개발되었다. Iconv는 최초에 HP-UX 운영체제에 탑재되었었고 이후 표준화를 거쳐 GNU C 라이브러리에 libiconv로 등록되었다. 자세한 내용은 GNU를 참고하자.

이번 글에서는 Node.js에서 Iconv를 사용하는 방법과 EUC-KR을 변환하는내용에 대해 다루겠다.


설치

Node.js 환경에서 Iconv를 사용하기 위해서는 node-iconv 라이브러리를 설치해야 한다. 만약 본인이 변환하려는 인코딩이 상대적으로 잘 알려진 인코딩이고 네이티브 환경에 영향을 주고 싶지 않다면 iconv-lite를 고려해보자.

설치는 물론 npm으로 진행된다.

$ npm install iconv

(테스트 결과 현재 Node.js 안정버전인 5.10.1 환경에서는 네이티브 컴파일러 설치 중 오류가 발생했다. 여러가지 이유를 확인했지만 지속적으로 문제가 발생해 테스트는 0.10.36 버전으로 진행했다.)

기본 사용법

설치가 잘 완료되었다면 이제 Iconv를 사용할 준비가 되었다. node-iconv의 기본 사용법은 아주 간단하다.

var Iconv  = require('iconv').Iconv;
// EUC-KR -> UTF-8
var iconv = new Iconv('EUC-KR', 'UTF-8');
console.log(iconv.convert('Hello Iconv'));

다음으로는 옵션에 대한 내용이다. iconv 선언시 두 번째 인자로 변경할 인코딩을 받게 되는데 여기서 //를 끼고 TRANSLIT, IGNORE 옵션을 선언할 수 있다. 해당 옵션들은 변경하려 하는 인코딩에서 특정 문자를 지원하지 않는 경우에 어떤 형태로 처리할지에 대해 정의하는 부분이다.(예를 들어 EUC-KR은 완성형 한글이기 때문에 UTF-8 -> EUC-KR 변환시에 문제가 생길 수 있다.)

  1. TRANSLIT 옵션은 대체 가능한 문자가 있는 경우 해당 문자로 대체할 수 있게 한다.
  2. IGNORE 옵션은 지원하지 않는 문자의 경우 해당 부분을 무시한다.
  3. 만약 TRANSLIT 혹은 IGNORE 옵션이 정의되지 않은 상태에서 위 상황이 발생하면 EILSEQ 에러가 발생한다.

사용 방법은 다음과 같다.

var iconv = new Iconv('UTF-8', 'ASCII//IGNORE');
iconv.convert('ça va'); // returns "a va"

var iconv = new Iconv('UTF-8', 'ASCII//TRANSLIT');
iconv.convert('ça va'); // "ca va"

var iconv = new Iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE');
iconv.convert('ça va が'); // "ca va "

EUC-KR 웹 페이지 스크랩

이번에는 실제로 EUC-KR 문서를 어떻게 UTF-8로 변환하는지에 대해 자세히 알아보도록 하자. 간단한 GET 요청으로 문서를 가져오기 위해 request 라이브러리와 문서 내 탐색을 쉽게 하기 위해 cheerio 라이브러리를 사용했다.

처음으로는 해당 문서를 GET 요청을 통해 가져온다. 여기서 주의할 점은 문서를 binary 형태로 가져와야 한다는 것이다. 그래야만 정상적으로 문서를 인코딩 할 수 있다.

var Iconv = require(‘iconv’).Iconv,
request = require(‘request’),
cheerio = require(‘cheerio’);
var option = {
url: url,
encoding: ‘binary’, // 꼭 binary로 받아야 한다.
};
// GET 요청
request(option, function(err, response, html) {
if (err) {
return;
}
   // Charset을 확인하기 위해 contentType을 저장한다.
var contentType = response.headers[‘content-type’];
...
});

마지막으로 요청을 통해 받아온 결과를(html) Buffer 형태로 변환하고 해당 페이지가 EUC-KR로 인코딩 되어 있는지 확인하자.

var binaryHtml = new Buffer(html, ‘binary’),
parseHtml = convertUTF8(binaryHtml, contentType);
function convertUTF8(html, contentType) {
var $ = cheerio.load(html.toString()),
iconv = new Iconv(‘EUC-KR’, ‘UTF-8//TRANSLIT//IGNORE’);
    var charset = $(‘meta[http-equiv=”Content-Type”]’).attr(‘content’) || contentType;
    if (charset && charset.toUpperCase().indexOf(‘EUC-KR’) > -1) {
return iconv.convert(html);
}
    return html;
}

cheerio를 통해 해당 페이지를 jQuery 셀렉터 형태로 탐색할 수 있도록 하고 이전에 저장한 contentType과 페이지 내 meta 태그내에 정의되어 있는 Content-Type을 확인해 해당 페이지가 EUC-KR인지 확인 후 인코딩을 변경한다.(조약하지만 딱히 방법이 없어 보인다.)

위 과정을 무리없이 거쳤다면 정상적으로 인코딩 변환이 완료된 것을 확인할 수 있을 것이다.


One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.