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.commons.lang3.StringEscapeUtils, 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 209x (2.25 kB)
Aplikace je včetně zdrojových kódů v jazyce Java