poniedziałek, 2 maja 2011

Java: parsowanie XHTML z pomocą biblioteki Jericho

Na kanale YouTube Kanciastej Codzienności CTSG zamieścił niedawno nowy konkurs. Do wygrania było kilka gier i transfery w jakimś serwisie, którego nazwę szybko zapomniałem. Mnie tam w sumie interesowała subskrypcja do gry Minecraft. Konkurs polegał na odnalezieniu komentarza zamieszczonego pod jednym z filmów opublikowanych w przeszłości przez CTSG. Komentarz był stworzony wg pewnego wzorca, jednak został zamieszczony z innego konta użytkownika. Nie trudno zauważyć, że najprostszym rozwiązaniem tego zadania było zapędzenie do niego maszyny i pójście po herbatę :). Wystarczy pobrać stronę, sparsować i przejrzeć wszystkie komentarze. Dzięki temu poznałem bibliotekę Jericho, która posłużyła do wykonania tego zadania i znalezienia poprawnej odpowiedzi w bardzo krótkim czasie. Dodam jeszcze, że konkurs się zakończył i było przesłanych ponad 2000 poprawnych odpowiedzi. Moja była wśród nich, jednak nie zostałem wylosowany niestety. Mogę dzięki temu jednak zamieścić już metodę :).

Jericho jest biblioteką napisaną w pełni w języku Java przeznaczoną dla tego języka i umożliwiającą na bardzo zaawansowane operacje na kodzie w językach znaczników. Korzystając z Jericho można stosunkowo łatwo przejrzeć, przetworzyć, czy edytować pliki HTML, XHTML czy XML. Na stronie projektu można przeczytać, że Jericho radzi sobie też dobrze z kodem PHP czy ASP. Biblioteka nie tylko wydała się, ale szybko okazała się idealna do tego zadania. Jej twórcy nie zapomnieli stworzyć i opublikować dobrego Jdoc'a dzięki czemu zapoznanie się z biblioteką nie zajęło dłużej niż 10minut!

Generalnie, to nie interesowała mnie zupełnie struktura kodu. Nie ma za specjalnie znaczenia który element w którym się zawiera. Jest to możliwe aktualnie dlatego, że kluczowe elementy opatrzone są albo odpowiednimi wartościami ID, albo odpowiednią klasą. W przypadku stron filmów na youtube każdy komentarz (dokładniej jego treść) to DIV o klasie comment-text. Dzięki temu analizowanie kodu sprowadziłem do przejrzenia wszystkich elementów w HTML o tej klasie. Następnie zawartość elementu porównywałem ze wzorcem właściwego komentarza przy pomocy wyrażenia regularnego. Jest ono nieco nadmiarowe, ponieważ nie byłem pewien, czy litery będą wszystkie tego samego rozmiaru, oraz czy w parze pierwsza będzie litera, czy cyfra. Nieco dłuższe wyrażenie mnie tam w niczym nie przeszkadzało.

Pojawił się w pewnym momencie jeden problem. DIV comment-text potrafi zawierać wiele elementów. Mogą znajdować się tam linki (<a>), może też zawierać wiele akapitów (<p>) ponieważ youtube w komentarzach każdą nową linię traktuje jako nowy paragraf, nie jako łamanie linii. Rozwiązaniem tego problemu jest po prostu przejrzenie wszystkich elementów wewnątrz comment-text i dopasowanie ich do wzorca.

Poniżej znajduje się kod, który wykonuje całe zadanie.Z racji, że komentarz mógł znajdować się pod dowolnym filmem ID wszystkich filmów na początku dodane zostały do listy. Następnie cała ta lista zostaje przejrzana i ewentualny komentarz jest podawany razem z linkiem do filmu, pod którym wystąpił. Łatwo zauważyć, że właściwego kodu do przeglądania tych elementów jest bardzo mało. To zdecydowanie zaleta dobrze zrealizowanej biblioteki.

package org.itc.ctsg;

import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.Source;

public class Main {

    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException
    {
        String base="http://www.youtube.com/all_comments?v=";
        List urls = Arrays.asList(new String[]
                                                      {
               
                "odTV24ralrg",
                "QvAykBjFahs",
                "iB5IYI1o6AU",
                "H-WIqpxg7J0",
                "cpWTj25NHP0",
                "lqTWgA-u_2U",
                "v8EMhLkJEb8",
                "pqie3Dx4m18",
                "pHiSG9bPpXc",
                "APoqGa9JnbU",
                "6ikMWFCZvLc",
                "J8r7OCtIN3U",
                "X7pHkFi6kh8",
                "3pRYHFi1mRk",
                "Qv1VFFT7xjA",
                "347RqASCi4g",
                "0sCyOJVhfOA",
                "tHN8d6xvq64",
                "R29uLuyXqhU",
                "BDNrU-xPJ5o",
                "zijjQoyl4zg",
                "2S4i9eUqFME",
                "i7jX2eZRnYM",
                "QeQWVPcBYyw",
                "Tp1ddHxoxP4"
                                                      });
       
        for(String surl : urls)
        {
        String host = base+surl;
        System.out.println("For url: "+host);
        System.out.println("====================================================");
        URL url = new URL(host);
       
        Source source = new Source(url);
       
        List lista = source.getAllElements();
       
        for(Element elem : lista)
        {
            if(elem!=null
                    && elem.getAttributes() != null
                    && !elem.getAttributes().isEmpty()
                    && elem.getAttributes().getValue("class") != null
                    && elem.getAttributes().getValue("class").equals("comment-text"))
            {
                for(Element in : elem.getContent().getAllElements())
                    if(in != null && in.getContent().toString()!=null && in.getContent().toString().matches(".*(([a-zA-Z][\\d]){5}|([\\d][a-zA-z]){5}).*"))
                    {
                        System.out.println("<"+in.getName()+">");
                        System.out.println(in.getContent().toString());
                    }
            }
        }
        }
    }
}


Przy analizowaniu kodu HTML wykorzystana została wtyczka FireBug. Sprawuje się ona niezwykle dobrze przy takich zastosowaniach. Zastanawia mnie tylko kwestia jakie nagłówki Java wysłała do YouTube kiedy zasób był pobierany poprzez URL. Podczas testowania aplikacji dobre kilkadziesiąt razy była odpalana i kod był analizowany. Będę to musiał sprawdzić w wolnej chwili na własnym serwerze najlepiej :).

Czytaj też:

Brak komentarzy:

Prześlij komentarz