2010년 12월 13일 월요일

HtmlCleaner

작지만 강력한 HTML 파서, HtmlCleaner, html parser Java

2008/04/30 15:32

복사http://blog.naver.com/tyboss/70030506248

HTML을 파싱해보면 알겠지만, HTML 파싱만큼 비정형적인 것에 대한 분노를 끓어오르게 하는 것은 좀처럼 없는 것 같다. 다소 오바긴 하지만, 지난 며칠간의 상황이 그렇다.

HTML은 알다시피 상당히 융통성 있는 구조를 띄고 있는데, 이 융통성이란 것이 코드를 작성하는 데 있어 가장 골치아프고 황당한 경우를 많이 겪게 만드는 걸림돌로 작용할 때가 많다. 그중 몇 가지를 들자면, 여는 태그와 닫는 태그가 굳이 쌍을 이루지 않아도 브라우저에서 그럭저럭 보여진다는 것, 한 페이지안에 각종 HTML, BODY, 등의 태그가 중복되어 나타나는 것, 설상가상으로 한 페이지에 여러개의 문자 인코딩이 섞여 그것들끼리 꼬이고 충돌하는 등의 문제를 겪게 된다. 특히나 요즘같이 웹 표준에 대한 관심이 고조되고 있는 상황에서조차도 그러한 비정형의 극치를 달리는 HTML 페이지들이 만들어져 버젓이 브라우저의 인내심을 테스트하고 있다.

아무튼, 오늘 소개하는 HTML 파서는 누차 강조하지만 비정형의 극치, HTML을 well-formedness를 요구하는 XML 포맷으로 변환시켜 파싱할 수 있도록 해주는 툴이다. 이름하야 HtmlCleaner!

공식 웹 사이트 : http://htmlcleaner.sourceforge.net/

요즘 자주 쓰고 있는 HTML 파서는 널리 알려져 있는 Java 기반의 HtmlParser인데, 이 HtmlParser는 우연찮게도 Refactoring to Patterns(패턴을 활용한 리팩터링)에 리팩터링 예제로 단골손님처럼 나오는 프로그램이다. 아무튼 문제는 이 HtmlParser가 상당히 섬세하고 다양한 기능을 제공하기는 하지만 치명적인 약점으로 주석처리에 상당히 약하다는 것과 문법에 맞지 않는 HTML 태그가 포함된 HTML 문서의 파싱을 가끔씩 거부한다는 것이다. 예를 들면, 중간에 주석이 포함되어 있고, 그 안쪽으로 태그가 있을 경우 그 안쪽에 있는 태그 처리를 무시하고 넘어가 버린다는 것이다. 정작 필요한 내용이 주석이 시작된 다음에 나올 경우가 있는데, 이럴땐 정말 난감하다. -_-;

오늘도 마침 이러한 문제가 생겨서 고민고민 해가면서 주석을 없애보기도 하고 해보았지만, 워낙 문법을 지키지 않은 페이지라서 주석 제거하는 것조차도 힘에 버거웠다. 그러던 찰나 지난달 쯤인가 HTML 파서중에 HTML 페이지를 XML 파일로(Well-formedness!!!) 둔갑시켜 줘서 파싱을 수행한다는 파서를 소개했던 글이 갑자기 생각나 급하게 인터넷을 찾아보니, 나온게 바로HtmlCleaner. ^^;

HtmlCleaner는 말 그대로 손쉽게 비정형 HTML 페이지를 파싱해서 중간에 필요없다 싶은 주석이나 몇가지 파싱에 불필요한 요소들을 사전에 미리 제거하고 파싱을 수행하는데, 파싱이 완료되면 알아서 XML 포맷으로 변환된다. 변환된 다음에는 출력을 XML 포맷의 문자열이나 파일과 같은 OutputStream으로도 내보낼 수 있다. 이렇게 나오는 결과물에 대한 파싱은 상당히 쉬워짐은 말할 것도 없다.

위 그림은 HtmlCleaner 클래스의 핵심! setOmitXXX() 메소드들이다. HTML 문서를 파싱할 때 몇 가지 태그나 주석들을 미리 제거할 수 있도록 설정하는 메소드이다. 실제 사용예를 보자면, 가장 간단한 사용법은 이렇다.

import java.io.IOException;

import org.htmlcleaner.HtmlCleaner;

public class HtmlCleanerTest {

public static void main(String[] args) {

HtmlCleaner cleaner = new HtmlCleaner("test.html");

try {

cleaner.setOmitComments(true);

cleaner.setOmitDeprecatedTags(true);

cleaner.setOmitUnknownTags(true);

cleaner.clean();

cleaner.writeXmlToFile("output.xml");

} catch (IOException e) {

e.printStackTrace();

}

}

}


상당히 간단하다. HtmlCleaner 객체를 만든 다음 몇가지 설정을 해주고 clean() 메소드만 호출하게 되면 파싱 대상 문서가 XML 포맷으로 변환된다. 물론 Well-formed 된 채로.

그렇지만 HtmlCleaner는 HtmlParser 만큼의 파싱 고유의 기능을 풍부하게 제공하지는 않는다. 따라서 HTML을 파싱할 것이라면, 특히나 비구조적인 내용이 충만한 ^^ 골치덩어리 HTML 문서를 파싱할 것이라면 HtmlCleaner와 HtmlParser를 함께 사용하는 것이 좋을 것이다. :)