Java: Html parser
Html parser je program, který dokáže analyzovat HTML kód a dokáže jej rozebrat na jednotlivé části, tagy, a text.
Naprogramovat kvalitní HTML parser může být problém. Parser musí projít HTML dokument, tento dokument analyzovat a rozebrat. Určitě si dokážete představit jak je takový algoritmus složitý.
V javě existuje pro tuto problematiku již hotové řešení v podobě HTMLEditorKit.ParserCallback, který se nachází v javax.swing.text.
Použití ParserCallback si ukážeme nejlépe na příkladu.
Class ParseHtml
Naprogramujeme si třídu ParseHtml, která bude dědit HTMLEditorKit.ParserCallback adokáže analyzovat HTML kód a informace přehledně vypsat. Dokáže vypsat:
- začátek tagu
- konec tagu
- atributy, které tag obsahuje
- samostatný text
- html poznámky
- chyby při parsování
Metody pro všechny tyto operace jsou již v ParserCallback implementovány, takže je stačí použít.
Jako první vytvoříme konstruktor, který bude obsahovat dva vstupní parametry. Url adresu, kterou chceme parsovat a kódování, ve kterém bude html dokument načten.
V konstruktoru vytvoříme public proměnné a pro URL adresu provedeme jednouduchou kontrolu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public class ParseHtml extends HTMLEditorKit.ParserCallback { public String url = null; public String charset = null; /** * * @param url * @param charset * konstruktor tridy HtmlToText */ public ParseHtml(String url, String charset) { //jednoducha kontrola url adresy, jestli je ve tvaru s HTTP:// if (this.check_url(url.trim()) == 0) { System.out.println("Url adresa neni ve spravnem tvaru. Prosim zadejte adresu ve tvaru http://"); return; } //nastaveni promennych this.url = url; this.charset = charset; } /** * * @param url * @return * jednoducha kontrola url adresy */ public int check_url(String url) { if (url.startsWith("http://") == false) return 0; return 1; } } |
Tím máme hotový základ, který pouze vytvoří proměnné a provede jednoducou kontrolu.
Co musíme uděla nyní je připojit se k dané URL adrese. Z url adresy si tedy vytvoříme proměnnou typu URL a nezbývá než vytvořit BufferedReader.
Pro tuto operaci si vytvoříme novou metodu, jejíž volání přidáme do konstruktoru:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/** * zacatek parsovani. Vytvori bufferdreader a spusti parser */ private void run_url() { try { //vytvoreni nove URL URL url_id = new URL(this.url); //Vytvoreni BufferedReaderu BufferedReader htmlPage = new BufferedReader(new InputStreamReader( url_id.openStream(), this.charset)); //spusteni parsovani this.parse(htmlPage); } catch (MalformedURLException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } |
Pokud je URL adresa otevřena voláme metodu parse, do které předáme jako parametr BufferedReader. Metoda parser vypadá následovně:
1 2 3 4 5 6 7 8 9 10 |
/** * * @param htmlText * @throws IOException * vytvori novy ParserDelegator a vola metodu pro parsovani */ public void parse(Reader htmlText) throws IOException { ParserDelegator delegator = new ParserDelegator(); delegator.parse(htmlText, this, Boolean.TRUE); } |
Vytvoříme nový ParserDelegator a začneme parsovat. Pro přístupu k jednotlivým informacím o HTML dokumentu nám slouží impmentované metody.
HhndleText
První z několika metod je handleText. Slouží pro odchycení textu, který se nachází mimo HTML značky. Vstupní parametry jsou zde pouze dva. Text a jeho pozice v dokumentu.
Použití může být obdobné tomuto:
1 2 3 4 5 6 7 8 |
/** * @param text * @param pos * handleText odchiti veskery text, ktery vytvari obsah html stranky */ public void handleText(char[] text, int pos) { System.out.println("Text: " + String.valueOf(text)); } |
handleComment
Metoda handleComment umožňuje přístup k html komentářům. Vstupní parametry jsou obdobné jako u textu, a to text komentáře a pozice v dokumentu.
Opět jednoduché použití:
1 2 3 4 5 6 7 8 |
/** * @param coment * @param pos * handleComent odchiti html poznamky */ public void handleComment(char[] coment, int pos) { System.out.println("Komentar: " + String.valueOf(coment)); } |
handleStartTag
Určitě užitečná a zajímavá metoda je handleStartTag, která dokáže informovat o začátku nového tagu a navíc dokáže odchytit i atributy, které jsou v tagu umístěny:
1 2 3 4 5 6 7 8 9 10 |
/** * @param tag * @param atr * @param pos * handleStartTag odchyti zacatek noveho tagu, vcetne jeho atributu */ public void handleStartTag(HTML.Tag tag, MutableAttributeSet atr, int pos) { System.out.println("Zacatek tagu: "" + tag + """); System.out.println("Tag obsahuje atributy: "" + atr + """); } |
handleEndTag
K metodě handleStartTag musí existovat i metoda handleEndTag, která na rozdíl od první metody dokáže zjistit ukončení tagu. Vstupní parametry jsou v tomto případě pouze dva a to HTML.Tag a pozice v dokumentu:
1 2 3 4 5 6 7 8 9 |
/** * @param tag * @param atr * @param pos * handleEndTag odchyti konec tagu */ public void handleEndTag(HTML.Tag tag, int pos) { System.out.println("Konec tagu: "" + tag + """); } |
handleSimpleTag
Metoda handleStartTag informuje pouze o přítomnosti tagu, který je párový. Neinformuje o tagu jako je například BR, IMG, HR a podobně, které párové nejsou.
Pro tuto potřebu je zde metoda handleSimpleTag. Metoda obsahuje stejně jako handleStartTag tři parametry:
1 2 3 4 5 6 7 8 9 10 |
/** * @param tag * @param atr * @param pos * handleSimpleTag odchyti jednoduche html tagy jako BR, IMG, apod. */ public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet atr, int pos) { System.out.println("Zacatek jednoducheho tagu: "" + tag + """); System.out.println("Tag obsahuje atributy: "" + atr + """); } |
handleError
Pokud při parsování nastane jakýkoli problém, dokáže nás o tom informovat metoda handleError. Jako parametry jsou String ve tvaru chyby a pozice v dokumentu:
1 2 3 4 5 6 7 8 |
/** * @param str * @param pos * handleError odchyti chby v HTML */ public void handleError(String str, int pos) { //System.out.println("Parse error: " + str); } |
Výstup třídy nás dokáže podrobně informovat o celé struktuře html dokumentu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Zacatek tagu: "h3" Tag obsahuje atributy: "class=title s_win_title " Zacatek tagu: "span" Tag obsahuje atributy: "class=ico " Zacatek jednoducheho tagu: "img" Tag obsahuje atributy: "alt=Stream.cz - zábavná videa height=16 src=/favicons/194.png width=16 " Konec tagu: "span" Zacatek tagu: "span" Tag obsahuje atributy: "class=text " Zacatek tagu: "a" Tag obsahuje atributy: "href=http://www.stream.cz title=Stream.cz - zábavná videa | Aktualizováno v 13:05 " Text: Stream.cz - zábavná videa Konec tagu: "a" Konec tagu: "span" |
Protože známe atributy, nebyl by problém zjistit například veškeré url adresy, které daná HTML stránka obsahuje:
1 2 3 4 5 6 7 |
public void handleStartTag(HTML.Tag tag, MutableAttributeSet a, int pos) { if( String.valueOf( tag ) == "a"){ if( String.valueOf( a ).startsWith("href=http://") ) System.out.println( String.valueOf( a ).substring(5) ); } } |
nebo podobným způsobem hodnoty jiných atributů.
Nakonec kód celé třídy pro parsování:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import javax.swing.text.MutableAttributeSet; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.parser.ParserDelegator; public class ParseHtml extends HTMLEditorKit.ParserCallback { public String url = null; public String charset = null; /** * * @param url * @param charset * konstruktor tridy HtmlToText */ public ParseHtml(String url, String charset) { //jednoducha kontrola url adresy, jestli je ve tvaru s HTTP:// if (this.check_url(url.trim()) == 0) { System.out.println("Url adresa neni ve spravnem tvaru. Prosim zadejte adresu ve tvaru http://"); return; } //nastaveni promennych this.url = url; this.charset = charset; //spusteni parseru run_url(); } /** * zacatek parsovani. Vytvori bufferdreader a spusti parser */ private void run_url() { try { //vytvoreni nove URL URL url_id = new URL(this.url); //Vytvoreni BufferedReaderu BufferedReader htmlPage = new BufferedReader(new InputStreamReader( url_id.openStream(), this.charset)); //spusteni parsovani this.parse(htmlPage); } catch (MalformedURLException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * * @param url * @return * jednoducha kontrola url adresy */ public int check_url(String url) { if (url.startsWith("http://") == false) return 0; return 1; } /** * * @param htmlText * @throws IOException * vytvori novy ParserDelegator a vola metodu pro parsovani */ public void parse(Reader htmlText) throws IOException { ParserDelegator delegator = new ParserDelegator(); delegator.parse(htmlText, this, Boolean.TRUE); } /** * @param text * @param pos * handleText odchiti veskery text, ktery vytvari obsah html stranky */ public void handleText(char[] text, int pos) { System.out.println("Text: " + String.valueOf(text)); } /** * @param coment * @param pos * handleComent odchiti html poznamky */ public void handleComment(char[] coment, int pos) { System.out.println("Komentar: " + String.valueOf(coment)); } /** * @param tag * @param atr * @param pos * handleStartTag odchyti zacatek noveho tagu, vcetne jeho atributu */ public void handleStartTag(HTML.Tag tag, MutableAttributeSet atr, int pos) { System.out.println("Zacatek tagu: "" + tag + """); System.out.println("Tag obsahuje atributy: "" + atr + """); } /** * @param tag * @param atr * @param pos * handleEndTag odchyti konec tagu */ public void handleEndTag(HTML.Tag tag, int pos) { System.out.println("Konec tagu: "" + tag + """); } /** * @param tag * @param atr * @param pos * handleSimpleTag odchyti jednoduche html tagy jako BR, IMG, apod. */ public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet atr, int pos) { System.out.println("Zacatek jednoducheho tagu: "" + tag + """); System.out.println("Tag obsahuje atributy: "" + atr + """); } /** * @param str * @param pos * handleError odchyti chby v HTML */ public void handleError(String str, int pos) { //System.out.println("Parse error: " + str); } /** * @author Zachar Jiří * @param args */ public static void main(String[] args) { String charset = "UTF-8"; String url = null; if (args.length > 0) { url = String.valueOf(args[0]); } if (args.length > 1) { charset = String.valueOf(args[1]); } new ParseHtml(url, charset); } } |
Dobrý den, chci se zeptat, jak bych vypsala pouze text mezi tagy <h2> a </h2>?? děkuji