Java로 만들어진 Html Parser는 제법 많이 있다.

- org.htmlparser
- nekoParser

정도가 있는데, htmlParser는 다소 무겁기는 하지만 잘 정의된 구조에, 4만 페이지 정도의 Hard한 환경에서 Parsing Test를 해보았으나, 모든 문서를 Parsing해 내는(파싱 알고리즘 통과) 안정성을 자랑하였다.

하지만, HTML Parser를 한번이라도 개발한 경험이 있는 사람들은 알겠지만..
HTML Parser의 최대 난제는 아무래도 script value element 처리를 비롯한 html 문법을 무시해야 하는 영역의 구현이 아닐까 싶다.
불행히도 대부분의 Open Source 파서는 이러한 부분에 대한 정확한 처리를 하지 못한다. Script 내부의 Text까지 HTML 문법을 적용하여 Parsing하고, Source 구조상 이부분의 수정이 쉽지가 않았다.

Parser의 생명은 Parsing의 정확도와 속도다.

 정확도에 있어서, HTML은 XML과 틀리게 상당히 변칙적인 문법을 허용한다. 이것은 Parse 트리 구축시 모호성이 발생한다는 것을 의미한다. IE를 비롯한 대부분의 Browser에 내장된 HTML Parser의 경우 HTML 문서의 전처리를 거쳐 well-formed 문서로 만들고(1Pass), XML Parser등을 적용, DOM Tree를 생성한다.(2Pass)
 최소 2Pass를 거쳐야 하므로 속도는 궁극적으로 떨어지며, 일반적으로 구현시 고성능의 ReadBuffer의 구현으로 하거나 1Pass시 불완전 DOM Tree 구축후 2Pass 단계에서 보정방식을 사용하기도 한다.

 속도에 있어 또다른 문제는, SCRIPT부분의 처리이다. SCRIPT 내부 value 처리시 Script 처리 Parser로 동적 전환되고, </SCRIPT>라는 문자열이 발견되기 이전 시점에 Html Parser로 전환되는 과정을 거쳐야 한다. 이것은 Parser 구현시 미리읽기 Buffer를 구현하거나 혹은 Read Cursor가 Lexing 과정중 Back 될 수도 있음을 의미한다. 이런 구현은 속도에는 치명적이며 구현 복잡도는 매우 증가한다.
(그런데 이문제에 대해 별다른 해결책은 없어 보인다, 물론 돈과 시간이 적절히 주어진다면 대체할수 있는 구현은 가능 하겠지만.. 하드웨어가 저질코드의 성능을 충분히 커버해 주는 시대이므로 거기 까지 갈일은 없을것 같다..ㅋㅋ)

 주말 이틀 반납해가며, SCRIPT 부분에 대해 Parsing이 되는 HTML Parser를 만들어 보았다.

 Version은 0.1 정도로 보면 되겠다. 가장 문법이 변태적인 Naver정도는 정확하게 Parsing을 해냈으나 아직까지 많은 테스트와 수정, 추가가 필요한 상황이다. 연구용 정도로 쓰기는에는 불편함이 없을듯 하며, 아직 DOM Tree를 생성하는 부분의 구현은 안되었다.(시간 부족)

 Lexer가 어느정도 안정화 되었으므로 Stack 정도만 적절히 쓴다면 DOM Tree 정도는 쉽게 구현이 가능할 것이다.

소스코드(Eclipse Project) 다운로드


아래는 대략적인 사용법이다.

파싱대상 소스(test.html)

<html>
<head>
  <title>가라지하 사바하</title>
</head>
<script>
<!--
  <a href="dcinside.com">아햏햏</a> fdsf <
  adf, < "" <:>
-->
</script>
<body bgcolor=red background="img/bg.jpg">
  <a href="http://kr.yahoo.com">야후</a>
</body>
</html>

출력결과

tag>html
tag>head
tag>title
latest Tag :title
text>가라지하 사바하
tag>/title
tag>/head
tag>script
text>
<!--
  <a href="dcinside.com">아햏햏</a> fdsf <
  adf, < "" <:>
-->

tag>/script
tag>body
 -bgcolor=red
 -background=img/bg.jpg
tag>a
 -href=http://kr.yahoo.com
latest Tag :a
text>야후
tag>/a
tag>/body
tag>/html


Lexer Code

File file = new File("d:\\test.html");
   Lexer lexer = new Lexer(file);
   Node node = null;
   while((node = lexer.getNextToken()) != null)
   {
    if(node instanceof TagNode)
    {
     String isClosed = "";
     TagNode tag = (TagNode)node;
     if(tag.isClosedTag()) isClosed = "/";
     System.out.println("tag>" + tag);
    }
    else if(node instanceof TextNode)
    {
     TextNode txNode = (TextNode)node;
     System.out.println("text>" + txNode.getParsedText());
    }
   }

막 구현된 소스라 리펙토링이 전혀 안된 상태이며, 구현과 설계를 동시에 하는 본인의 업무 특성상,
스파게티 코드를 보고 충격을 받지 말길 바란다. 차츰차츰 바로 잡아갈테니.. ㅋㅋ

+ Recent posts