Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 15 - Knihovna Jsoup v Javě - Parsování HTML

V předešlém cvičení, Řešené úlohy k 10.-14. lekci práce se sítí v Javě, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

V tomto tutoriálu se seznámíme s knihovnou Jsoup, která se používá pro načítání, vyhledávání a manipulaci s HTML soubory. Pro práci je nutné si Jsoup knihovnu stáhnout (soubor .jar) a přidat ji k našemu projektu.
Pro Eclipse je postup následující: pravým tlačítkem na projekt -> Build Path -> Configure Build Path -> záložka Libraries -> Add External JARs... -> vyberete knihovnu, kterou chcete přidat a potvrdíte. Z důvodu úspory místa nebudu u příkladů uvádět importy. Kompletní zdrojové kódy jsou ke stažení na konci lekce.

Objekt Document

Nejprve budeme pracovat s objektem Document. Ukážeme si celý kód, který potom okomentujeme:

public class Jsoup01 {

    public static void main(String[] args) throws IOException {
        String urlString = "http://www.seznam.cz/";
        Document doc = Jsoup.connect(urlString).get();

        System.out.println(doc.title());
        System.out.println(doc.text());
        System.out.println("=============================");

        Elements links = doc.select("a");
        for (Element link : links) {
            System.out.println("Link: " + link);
            System.out.println("Text: " + link.text());
        }
    }
}

Jako parametr si naše knihovna vezme řetězec představující url adresu a vytvoří Jsoup Document (pošle HTTP GET request):

Document doc = Jsoup.connect(urlString).get();

Ten si ukládáme do proměnné doc, se kterou pak můžeme dále pracovat. Takto například získáme a zobrazíme titulek a text dokumentu:

System.out.println(doc.title());
System.out.println(doc.text());

Objekt Elements představuje seznam objektů typu Element. Element představuje html element a skládá se z názvu tagu, atributů a potomků. Jednoduše řečeno následující řádek kódu vyhledá všechny tagy <a> a uloží je do seznamu:

Elements links = doc.select("a");

Nyní projdeme všechny elementy v seznamu links a vypíšeme je. Všimněte si, že link.text() vypíše text html elementu, kdežto link vypíše celý html element.

for (Element link : links) {
        System.out.println("Link: " + link);
        System.out.println("Text: " + link.text());
}

Rozhraní Connection

Connection představuje rozhraní pro získání obsahu z webu a naparsování tohoto obsahu do objektu typu Document.

Opět nejdříve uvedu celý kód, který poté projdeme:

public class Jsoup02 {

    public static void main(String[] args) {
        String urlString = "http://forum.objectivismonline.com/?showforum=42";

        Connection con01 = Jsoup.connect(urlString);
        Document doc01 = null;
        try {
            doc01 = con01.get();
            System.out.println(doc01.text());
        } catch (IOException e) {
            System.out.println("Exception: " + e);
        }

        Connection con02 = Jsoup.connect(urlString)
                .userAgent("Mozilla")
                .timeout(10000);
        Document doc02 = null;
        try {
            doc02 = con02.get();
            System.out.println(doc02.text());
        } catch (IOException e) {
            System.out.println("Exception: " + e);
        }
    }
}

Začátek je podobný první ukázce. Dále zde máme metodu get(), ta pošle request na server jako GET a naparsuje výsledek (vrátí objekt typu Document). Může však vyhodit několik výjimek např. MalformedURLException v případě chybné url. Také může dojít k výjimce HttpStatusException v případě, že na náš request jsme nedostali OK HTTP odpověď. Server, na který posíláme náš request, zjistí kdo jsme z user-agent header a v tomto případě se mu to co zjistí nelíbí a neodpoví nám tak, jak bychom chtěli.

Connection umožňuje retězit metody. Nejdříve nastavíme url, pak user-agent header a nakonec timeout v milisekundách:

Connection con02 = Jsoup.connect(urlString)
                .userAgent("Mozilla")
                .timeout(10000);

Na závěr si celý text požadované stránky necháme vypsat:

doc02 = con02.get();
System.out.println(doc02.text());

Načítání ze souboru a z řetězce

Nejdříve si vytvoříme jednoduchý HTML dokument (my_page.html):

<html>
<head>
  <title>My Page</title>
</head>
<body>
  <h1>Nadpis h1</h1>
  <div class="prvni">
    <h2>Nadpis h2</h2>
    <p>Toto je 1. odstavec <a href="http://www.itnetwork.cz">a toto je odkaz</a>.</p>
    <p>Toto je 2. odstavec</p>
    <p>Toto je 3. odstavec</p>
  </div>
  <div class="druhy">
    <h2>Nadpis h2</h2>
    <p>Toto je 4. odstavec</p>
    <p>Toto je 5. odstavec</p>
    <p>Toto je 6. odstavec</p>
  </div>
</body>
</html>

A následně ho načteme:

public class Jsoup03 {

    public static void main(String[] args) throws IOException {
        // načtení html ze souboru
        Document doc = null;
        File input = new File("my_page.txt");
        if (input.exists()) {
            doc = Jsoup.parse(input, "UTF-8");
        }
        System.out.println("doc:\n" + doc);

        // načtení html z řetězce - úplné html
        String html01 = "<html><head></head>"
                + "<body><h2>Nadpis</h2>"
                + "<p>Toto je první odstavec</p>"
                + "<p>Toto je druhý odstavec</p>"
                + "</body></html>";
        Document doc01 = Jsoup.parse(html01);
        System.out.println("\ndoc01:\n" + doc01);

        // načtení html z řetězce - neúplné html
        String html02 = "<head><body><head>"
                + "<h2>Nadpis</h2>"
                + "<p>Toto je první odstavec"
                + "<p>Toto je druhý odstavec</p></body>";
        Document doc02 = Jsoup.parse(html02);
        System.out.println("\ndoc02:\n" + doc02);
        System.out.println("\ndoc02.html():\n" + doc02.html());

        // výpis html bez entit a výpis textu
        System.out.println("\ndoc02.html() - StringEscapeUtils:\n" + StringEscapeUtils.unescapeHtml4(doc02.html()));
        System.out.println("\ndoc02.text():\n" + doc02.text());
    }
}

Nejdříve zkusíme načíst a vypsat dokument ze souboru my_page.txt:

doc = Jsoup.parse(input, "UTF-8");
//...
System.out.println("doc:\n" + doc);

Takto vytvoříme org.jsoup.nodes.Document a vypíšeme ho jako html.

Jako vstup můžeme metodě parse() předat též obyčejný řetězec (html01 a html02) a Jsoup se z něho pokusí udělat html dokument. V prvním případě předáváme korektní, úplný html dokument, v druhém jsme předali řetězec s neúplným html a Jsoup se s tím docela vypořádal. Když se podíváte do výpisu, zjistíte, že Jsoup správně doplnil chybějící html tagy. Také si ale pravděpodobně všimnete, že české znaky jsou nahrazeny entitami.

Pokud chceme stránky stáhnout a následně v nich vyhledávat, tak nám text plný html entit příliš nepomůže. Existuje však knihovna org.apache.com­mons.lang3.Strin­gEscapeUtils, pomocí jejíž metody unescapeHtml4() bude výstup vypadat tak, jak potřebujeme. Text následně vypíšeme bez html tagů.

Vyhledávání

Pro ukázku vyhledávání opět použijeme soubor my_page.txt:

public class Jsoup04 {

    public static void main(String[] args) throws IOException {
        Document doc = Jsoup.parse(new File("my_page.txt"), "UTF-8");

        // vyhledání a výpis všech odstavců (elementů p)
        Elements elem = doc.select("p");
        for (Element el : elem) {
            System.out.println(el);
        }
        System.out.println();

        // výpis prvního odstavce
        Element firstParagraphElement = doc.select("p").first();
        System.out.println(firstParagraphElement);
        System.out.println(firstParagraphElement.text());

        List<Node> nodes = firstParagraphElement.childNodes();
        for (int i = 0; i < nodes.size(); i++) {
            System.out.println("Node number " + (i + 1) + ": " + nodes.get(i));
        }
        System.out.println("Child number: " + firstParagraphElement.child(0));
        System.out.println();

        // vyhledání všech p v divu označeném třídou 'druhy'
        Element secondDivElements = doc.select("div.druhy").first();
        Elements paragraphElements = secondDivElements.select("p");
        for (Element par : paragraphElements) {
            System.out.println(par);
        }
    }
}

Vyhledání a výpis všech odstavců:

Elements elem = doc.select("p");
for (Element el : elem) {
        System.out.println(el);
}

Získání prvního odstavce (elementu p):

Element firstParagraphElement = doc.select("p").first();

Node je základní abstraktní třída, ze které dědí např. Element, Document a další. A co je to vlastně childNode daného elementu zjistíte nejlépe z výpisu:

List<Node> nodes = firstParagraphElement.childNodes();
for (int i = 0; i < nodes.size(); i++) {
        System.out.println("Node number " + (i + 1) + ": " + nodes.get(i));
}

Takto získáme potomka elementu firstParagraphElement na pozici nula (prvního potomka):

firstParagraphElement.child(0)

Vyhledávat lze též do hloubky. Zde nejdříve vyhledáme první element <div class="druhy"> a následně v tomto elementu vyhledáme všechny odstavce a ty zobrazíme:

Element secondDivElements = doc.select("div.druhy").first();
Elements paragraphElements = secondDivElements.select("p");
for (Element par : paragraphElements) {
        System.out.println(par);
}

Ukázkou vyhledávání dnešní tutoriál ukončíme. Práci s knihovnou Jsoup bude věnována ještě některá z dalších lekcí.

V příští lekci, Knihovna Jsoup v Javě - Pokročilé vyhledávání, si ukážeme, jak lze pracovat například s obrázky, odkazy či atributy v html dokumentu pomocí Java knihovny Jsoup.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 208x (2.25 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
Řešené úlohy k 10.-14. lekci práce se sítí v Javě
Všechny články v sekci
Síť v Javě
Přeskočit článek
(nedoporučujeme)
Knihovna Jsoup v Javě - Pokročilé vyhledávání
Článek pro vás napsal vita
Avatar
Uživatelské hodnocení:
5 hlasů
vita
Aktivity