Porządki w HTML przy użyciu HTMLCleaner

28 02 2010

Czasami zachodzi potrzeba sięgnięcia do źródła danych jakim są strony internetowe. Niestety często zdarza się tak, że kod HTML nie jest dobrze sformatowany i nie da się go przejrzeć za pomocą narzędzi do XML.

import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class Moja {
    public static void main(String[] args) {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);

        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse("index.htm"); // błąd podczas konwersji dokumentu na XML

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Wykonanie tego kodu może spowodować pojawienie się w zależności od zawartości dokumentu index.htm różnych błędów np.:

  • Server returned HTTP response code: 503
  • The entity “nbsp” was referenced, but not declared.

Powodami najczęściej są brakujące domknięcia tagów, czy niezrozumiałe dla parsera XML znaki twardych spacji “ ”. Można taki kod poprawić ręcznie, można napisać własne narzędzie do normalizacji dokumentów HTML, ale na pewno szybciej i prościej będzie skorzystać z gotowego rozwiązania.

W tym artykule postaram się przybliżyć bibliotekę HTMLCleaner która udostępniana jest na licencji BSD, a więc każdy może ją bez przeszkód używać, modyfikować i rozpowszechniać.

Instalacja

Biblioteka jest dostępna do pobrania pod adresem http://htmlcleaner.sourceforge.net/download.php. Po pobraniu rozpakowujemy archiwum i dodajemy plik htmlcleaner.jar do naszego projektu.

Aby dodać do projektu bibliotekę HTMLCleaner w środowisku Eclipse Galileo należy:

  • Kliknąć prawym klawiszem myszy na projekcie i wybrać Build Path -> Configure Build Path…
  • W nowym oknie przechodzimy do zakładki Libraries i klikamy na przycisk Add External JARs…
  • Wybieramy plik htmlcleaner.jar i zatwierdzamy zmiany klikając na przycisk OK.

Podstawy

import java.io.File;
import java.io.IOException;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;

public class Moja {
    public static void main(String[] args) {
        HtmlCleaner cleaner = new HtmlCleaner();
        try {
            TagNode rodzic = cleaner.clean(new File("index.htm"));
            TagNode[] dzieci = rodzic.getAllElements(false); // true/false - wywołanie rekurencyjne/bez rekurencji  			

            for (TagNode dz : dzieci) {
                System.out.println(dz);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Linia 9:
Wykorzystanie obiektu stworzonego w linii 7 do wyczyszczenia pliku index.html (plik nie jest modyfikowany, jego zawartość przechowywana jest w pamięci). Czyszczenie kodu to nic innego jak poprawienie istniejącej struktury tak aby spełniała standardy XML (w przypadku braku tagu zamykającego, jest on dopisywany, itp.). Metoda clear() ma wiele wersji przystosowanych między innymi do pozyskiwania kody HTML z plików (File), serwerów (URL) czy w postaci łańcuchów tekstowych (String), my chcemy wczytać dane z pliku zapisanego w katalogu z którego uruchamiana jest aplikacja więc wykorzystaliśmy wersję pobierającą w parametrze obiekt typu File. Klasa TagNode wykorzystywana jest do przechowywania informacji o pojedynczym węźe, umożliwia dostęp do atrybutów węzła oraz jego dzieci za pośrednictwem między innymi takich metod:

  • TagNode[] getAllElements(boolean arg), List getAllElementsList(boolean arg)
Zwracają wszystkie dzieci danego węzła, arg true włącza wywołanie rekurencyjne (zostaną pobrane nie tylko dzieci danego węzła, ale także dzieci dzieci itd. innymi słowy wszystkie węzły które leżą niżej w hierarhii od rodzica), arg false spowoduje wykonanie nie rekurencyjnej wersji tej metody (zwróci wszystkie dzieci danego elementu ale tylko te które leżą o 1 stopień niżej w hierarchii niż rodzic).
  • String getAttributeByName(String name)

Zwraca wartość atrybutu danego węzła.

  • Map getAttributes()

Zwraca mapę atrybutów gdzie kluczem jest nazwa atrybutu.

  • TagNode getParent()

Zwraca węzeł który jest rodzicem danego węzła.

  • String getText()

Zwraca tekst jaki zawiera dany element, trzeba jednak zaznaczyć że funkcja ta działa rekurencyjnie i zwraca również tekst który umieszczony został w wewnętrznych węzłach (dzieciach).

Linia 10:
Pobranie wszystkich elementów z głównego węzła (html). Ustawienie parametru na false zwróci dwa węzły: head i body (bo są dziećmi węzła głównego: html). Ustawienie parametru na true rekurencyjnie zwróci węzły z całego dokumentu HTML.

Linia 12:
Wypisanie nazw wszystkich elementów pozyskanych w linii 10. Klasa TagNode ma przeciążoną metodą toString() w taki sposób aby zwracała nazwę węzła.


Działania

Informacja

Dodaj komentarz

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Zmień )

Twitter picture

You are commenting using your Twitter account. Log Out / Zmień )

Facebook photo

You are commenting using your Facebook account. Log Out / Zmień )

Connecting to %s




Follow

Otrzymuj każdy nowy wpis na swoją skrzynkę e-mail.