Občas nastane situace, kdy potřebujeme jednoduše získat konkrétní části webové stránky a strojově ji zpracovat (ať už se jedná o parsování konkrétních odstavců textu nebo o jednoduchý crawler). V tomto článku si ukážeme jak na to pomocí užitečné knihovny HtmlAgilityPack. Pokud jste navíc někdy dříve používali výchozí XML parser v .NET frameworku, budete tu doslova jako ryba ve vodě, protože implementace této knihovny je mu velice podobná.
Abychom mohli veškeré funkce knihovny využívat, musíme si ji nejprve vložit do našeho projektu. To můžeme udělat buď ručně stažením archivu ze stránek http://htmlagilitypack.codeplex.com/ , jeho rozbalením a přidáním příslušné dll knihovny do projektu nebo lépe : stažením přes balíčkovací systém NuGet, jehož konzoli nalezneme přímo ve VS v nabídce Tools/Library Package Manager/Package Manager Console. Spustí se nám příkazová řádka, do které zadáme jednoduchý příkaz:
Install-Package HtmlAgilityPack
který nám stáhne a následně vloží celou knihovnu do projektu. To je rozhodně elegantnější způsob, než lovit po internetu archivy s knihovnami, nemyslíte ? Pokud si nyní rozkliknete References v projektové struktuře, měli byste již vidět mezi používanými libraries i HtmlAgilityPack. Do třídy, ve které budeme chtít stránku zpracovávat ještě vložíme
using HtmlAgilityPack; using System.Xml;
kde první nám samozřejmě odkazuje na naši staženou knihovnu a druhý odkazuje na Xml komponenty frameworku, bez kterých bychom se ochudili o možnost používat XPath výrazy na zpřístupňování jednotlivých prvků DOMu. Máme nainstalováno, vloženo a můžeme začít!
Získání a načtení webové stránky
Samozřejmě nasávají dvě možnosti, jak stránku získat – buď ze souboru na disku nebo přímo z internetového serveru. První možnost je prostá, vytvoříme si objekt typu HtmlDocument a jeho metodou Load stránku načteme – prvním argumentem je cesta k souboru, druhým kódování dokumentu.
HtmlDocument doc = new HtmlDocument(); doc.Load("index.htm",Encoding.UTF8);
Pokud chceme pracovat se stránkou, která není na lokálním počítači, můžeme ji získat následujícím způsobem:
HtmlDocument doc = new HtmlDocument(); WebRequest request = WebRequest.Create("http://www.nejakyserver.com/stranka.html"); § WebResponse response = request.GetResponse(); Stream data = response.GetResponseStream(); doc.Load(data,Encoding.UTF8);
Po vytvoření HtmlDocument objektu pomocí WebRequest zašleme požadavek na serveru, jehož adresa je argumentem statické metody Create, jeho odpověď zachyt WebResponse , ze které následně vytáhneme zaslaná data stránky v podobě streamu. Ten načteme s patřičným kódováním (pokud typ kódování nahradíme booleanovskou proměnnou true, HtmlDocument se pokusí kódování stránky odhadnout na základě posloupnosti několika prvních bajtů streamu). Stránku máme, co s ní ?
Zpracováváme získanou stránku
Tady je opravdu dobré si nastudovat minimálně základy XPath výrazů – není to nic tak těžkého a dovolí vám to lépe se v DOMu stránky orientovat. Nicméně stránku máme načtenou a HtmlDocument se postaral o její převedení na objektový stromový model. Nyní si chceme vytáhnout jen to potřebné. Řekněme, že chceme vytáhnout všechny cíle odkazů na stránce, tedy převedeno do řeči HTML – obsah atributu href u všech <a> tagů. To provedeme následovně:
foreach (HtmlNode link in doc.DocumentNode.SelectNodes("//a[@href]")) { HtmlAttribute att = link.Attributes["href"]; links.Add(att.Value); }
Procházíme kolekci vzniklou použitím XPath výrazu, který znamená : všechny odkazy (a), kdekoli v dokumentu ( //), které mají atribut href ( [@href] ). Tento atribut si pak přes HtmlAttribute zpřístupníme a pomocí Value přidáme jeho hodnotu do nějaké předpřipravené kolekce (stačí klidně List stringů).
Pokud místo více prvků potřebujeme jen jeden , můžeme místo DocumentNode.SelectNodes použít DocumentNode.SelectSingleNode, který nám vrátí pouze jedinou instanci typu HtmlNode. Jestliže by podle podmínky bylo vráceno prvků více (jako v minulém případu), bude vrácen ten, který se na stránce vyskytuje jako první.
A jsme u konce – ukázali jsme si, jak webovou stránku načíst a získat si z ní nějaká ta data. Ostatní už je na vás, HtmlAgilityPack je robustní knihovna a obsahuje toho mnohem víc, než jsme si dnes ukázali – proto, pokud budete potřebovat univerzální parser webových stránek, je tu právě pro vás.
Komentáře (3)
Tomáš Fučík
15.10.2013 000 10:59
HtmlAgilityPack je super, mám s ním postavenou jednu aplikaci. Napsal jsem si nad ním další nástavbu s pár metodama, aby se mi s tím líp pracovalo. Předtím jsem používal mshtml, ale práce s tím byla o dost náročnější a taky jsem nad to musel dopsat pár tříd, abych si ulehčil práci. Nakonec jsem narazil na problém, kdy jsem chtěl zpracovávat některé data pomocí mshtml v novém vlákně a při kompilaci mi to řvalo, že WPF knihovny nemohou běžet v určitém typu vlákna, to jsem pořešil. Pak nastal další problém, kdy jsem chtěl vyvolat přes mshtml událost Click() a z nějakého důvodu, se mi už nezavolala událost DownloadCompleted nebo tak nějak se jmenuje. Tak jsem přešel na HtmlAgilityPack, které neumí vyvolat událost Click(), musel jsem to celý pořešit jinak 🙁 což mě štve, ale co se dá dělat no 😀
Lukáš Hulha
10.06.2014 000 13:47
Dobrý den, potřeboval bych poradit.
Do školy mám za úkol naprogramovat program, který dle zadané URL adresy stáhne stránku, porovná všechny slova se slovy v externím zdroji (např. txt) a podle počtů jejich výskytu vypíše nejčastější kategorie obsahu stránky (například ze slov Počítač, Mechanika, Monitor…určí téma IT a počítače).
Podle návodu na této stránce se mi ale stránku stáhnout nepodařilo. Stačilo by mi stáhnout stránku a osekat z ní HTML tagy, zbytek bych nějak vyřešil sám. Mohl by mi prosím někdo poradit?
Děkuji. Snad si toho někdo včas všimne.
Lukáš Hulha
15.09.2014 000 08:11
Tak jsme nakonec ve škole dali hlavy dohromady a mám vyřešeno tak pokud případně někdo narazí na tento komentář s podobným problémem, mohu kód poskytnout. Stačí napsat na ccdn@seznam.cz