Parsowanie dokumentu HTML w .NET

Pewnie nie raz zastanawialiście się jak szybko i bez nadmiernego wysiłku wyciągnąć informacje z dokumentu HTML. Niedomknięty znacznik czy brak apostrofów to już standard w większości stron. XHTML po części rozwiązuje niektóre problemy, ale stron w pełni walidowanych też za dużo nie uświadczymy. Mimo podobieństw większość stron HTML nie  możemy traktować jak dokumentów XML. Platforma .NET nie dostarcza nam narzędzi do parsowania dokumentów HTML, pozostaje nam wiec korzystanie z zewnętrznych bibliotek.

W niniejszym wpisie chciałbym przedstawić bibliotekę którą poznałem już jakiś czas temu: Html Agility Pack, która nie raz już ułatwiła mi pracę. Główną zaletą tej biblioteki jest możliwość poruszanie sie po dokumencie HTML jak po dokumencie XML. Do wybierania elementów dokumentu mozemy uzyci języka XPath lub korzystając z LINQ (od wersji 1.4.0). Sam proces używania i posługiwania się wspomnianą biblioteką jest dość prosty i pokrótce zaprezentują go poniżej.

Oczywiście pierwszą czynnością jaką musimy wykonać aby móc korzystać z tej biblioteki jest dodanie do projektu referencji do pliku HTMLAgilityPack.dll. Po tej czynnosci mozemy korzystac z elementow jakie dostarcza nam biblioteka.

Klasą reprezentującą nasz dokument HTML jest HtmlDocument. Obiekt tej klasy tworzymy korzystając z domyślnego konstruktora.

[csharp]
WebClient client = new WebClient();
string html = client.DownloadString("http://blog.pietowski.com");

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
[/csharp]

Dokument HTML możemy wczytać korzystając z metody Load do której możemy przekazać strumień, obiekt klasy TextReader lub ścieżkę do pliku. Alternatywą jest użycie medody LoadHtml, którą wykorzystałem w powyższym przykładzie, wczytującej dokument bezpośrednio z obiektu klasy System.String. Przed wczytaniem dokumentu możemy ustawić odpowiednie opcje parsowania ustawiając odpowiednie wartości polom o nazwach w formacie:  OptionsXXX.

W celu pobranie błędów parsowania korzystamy z  dostępnej właściwości ParseErrors:

[csharp]
Console.WriteLine("Parse errors:");
foreach(HtmlParseError error in doc.ParseErrors)
{
Console.WriteLine(error.Reason);
}
[/csharp]

Główny węzeł dokumentu dostępny jest pod właściwością DocumentNode korzystając z tego obiektu możemy przeglądać kolejne węzły wczytanego dokumentu. W celu pobrania elementu na podstawie identyfikatora używamy metody GetElementbyId.

[csharp]
HtmlNode blogDescription = doc.GetElementbyId("blog-description");
if(blogDescription != null)
{
Console.WriteLine("Blog description: {0}",blogDescription.InnerText);
}
[/csharp]

Jeśli chcemy wyszukać konkretne węzły w naszym dokumencie możemy skorzystać z LINQ:

[csharp]
IEnumerable<HtmlNode> links = from link in doc.DocumentNode.DescendantNodes()
where link.Name == "a" && link.Attributes["href"] != null
select link;

IEnumerable<HtmlNode> links2 = doc.DocumentNode.DescendantNodes()
.Where(x=>x.Name == "a" && x.Attributes["href"] != null);
[/csharp]

lub wykorzystując język XPath:

[csharp]
HtmlNodeCollection xpathLinks =
doc.DocumentNode.SelectNodes("//a[@href]");

Console.WriteLine("Links:");
foreach(var link in links)
{
Console.WriteLine(link.Attributes["href"].Value);
}
[/csharp]

Najnowszą wersję opisywanej biblioteki można znaleźć na stronie http://htmlagilitypack.codeplex.com/

Projekt demonstrujący wykorzystanie HtmlAgilityPack można pobrać tutaj.

25,168 thoughts on “Parsowanie dokumentu HTML w .NET”

  1. Rebecca – thank you for this really thoughtful and interesting take. I think I forget that families can weather things that are even more difficult and complicated than infidelity. I forget this even though I see it all the time – illness, loss, etc. I really do appreciate you coming at this from a different angle. Now you have this whole “death and boredom” thing echoing in my head… Perhaps another post? We shall see. xox

  2. Admiring the time and energy you put into your blog and in depth information you present.
    It’s nice to come across a blog every once in a while that
    isn’t the same out of date rehashed material. Fantastic read!
    I’ve saved your site and I’m adding your RSS feeds to my Google account.

  3. Іf you ɑre going for finest contents like I dο, only pay a
    quick vіsit this website еvery day as it prօᴠides quɑlity contentѕ, thanks

  4. Of their last eight video games against AFC West opponents, the Cincinnati Bengals are 7-1 SU and ATS per the OddsShark NFL
    Database.

  5. An Oceanside Variety taping, a Diabetes Awareness
    Affair, education for Cycle The Coast (sidenote: check-out our
    discount for KOCT here) and the Oceanside Harbor Times Pageant!

  6. She showed the classic signs of the infection of a a headache, aching muscles and weakness
    or a lack of energy this fools people into thinking they
    are coming down with the flu but soon after a high fever
    followed by chills two to four days later this is then repeated most people
    do survive the illness but this is after 10 to 20 days of being very
    ill but you do need to spot the signs early. Some find this
    to be a great way to catch up on the latest, and you can even
    hear about a news story second hand, do a quick search on Google,
    and end up reading about that very news story. For dual charging if you prefer one over
    the other.

  7. Admiring the time and effort you put into your blog and detailed information you
    provide. It’s good to come across a blog every once in a while that
    isn’t the same old rehashed material. Fantastic read! I’ve saved your site and I’m adding your RSS feeds to my Google account.

  8. First of all I want to say awesome blog! I had a quick question that I’d like
    to ask if you don’t mind. I was curious to find out how you center yourself and
    clear your thoughts prior to writing. I have
    had a hard time clearing my thoughts in getting my ideas out.
    I truly do take pleasure in writing however it just seems like the
    first 10 to 15 minutes are wasted simply just trying to figure out how to begin. Any recommendations or tips?
    Many thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *