sobota, 30 marca 2013

Parsowanie HTML w .NET

Parsowanie HTML chociaż wydaje się rzeczą trywialną potrafi przysporzyć wielu problemów. Związanie jest to nie tyle ze skomplikowaniem zagadnienia co z formatem w jakim otrzymujemy dane.
Szukając tagu w wielu przykładach możemy natrafić na nie zamknięte tagi, niepoprawnie sformatowane znaczniki itp.

Jednym z lepszych parserów dostępnych w .NET jest biblioteka Html Agility Pack http://htmlagilitypack.codeplex.com/

Do projektu bibliotekę można dodać poprzez ściągnięcie odpowiedniej dll z codeplex, bądź korzystając z NuGet-a:

PM> Install-Package HtmlAgilityPack

Po zainstalowaniu biblioteki możemy przystąpić do przetwarzania HTML:

Code:
using System;
using System.Collections.Generic;
using System.Net;
using HtmlAgilityPack;
using System.Linq;

namespace HtmlAgilityPack_Sample
{
    class Program
    {
        private static void Main(string[] args)
        {
            var webClient = new WebClient();
            string pageContent = webClient.DownloadString(@"http://www.onet.pl/");
            var htmlDocument = new HtmlDocument();
            htmlDocument.LoadHtml(pageContent);

            foreach (HtmlNode link in htmlDocument.DocumentNode.SelectNodes("//a[@href]"))
            {
                HtmlAttribute att = link.Attributes["href"];
                Console.WriteLine(att.Value);
            }

            foreach (var div in htmlDocument.DocumentNode.SelectNodes("//div"))
            {
                HtmlAttribute classAttribute = div.Attributes["class"];
                if (classAttribute != null)
                {
                    Console.WriteLine(classAttribute.Value);
                }
            }

        }
    }
}


Przykładowy kod powyżej pokazuje w jaki sposób pobrać ze strony wszystkie linki i znaczniki div. Zapytania pisane są w języku XPath.

Biblioteka jest dostępna jako opensource - możemy więc pobrać kod i dodawać nową funkcjonalność.

Na temat biblioteki można znaleźć krytyczne wypowiedzi, dotyczące m.in.:
  • w przypadku gdy metoda SelectNodes() nie zwróci rezultatów, zwracany jest null co uniemożliwia użycie takiego rezultatu jako rezultatu wyrażenia np. pętli foreach
  • kiedy szukamy dzieci podanego węzła, metoda SelectNode szuka od początku dokumentu (aby szukała od aktualnego węzła należy podać parametr descendant::)
Z moich testów wynika, że biblioteka dobrze radzi sobie z parsowaniem nawet bardzo kiepskiej jakości kodu HTML. Wyżej wymienione problemy można samemu poprawić (kod jest dostępny). Zachęcam do eksperymentowania i podzieleniem się uwagami na temat biblioteki.

Brak komentarzy:

Prześlij komentarz