C#-Bibliothek zum Analysieren von HTML?

Die Bibliothek sollte mindestens .NET Framework Version 3.5 und 4.0 unterstützen; Unterstützung für Version 4.5 und zukünftige Versionen wäre auch großartig.

Es sollte nach Möglichkeit auch mit "unordentlichem" HTML umgehen können.

Antworten (5)

WinkelScharf :

  • Aktiv entwickelt/gepflegt
  • Eingebaute Unterstützung für CSS-Selektoren

HTML-Agility-Paket

Ich habe Html Agility Pack verwendet , und obwohl auf seiner Homepage nur Version 2.0 ausdrücklich erwähnt wird, funktioniert es hervorragend mit Version 4.0 des .NET-Frameworks. Ich vermute, dass es auch mit Version 4.5 gut funktioniert.

Hier ist ein Beispielcode, der Html Agility Pack mit LINQ verwendet:

var document = new HtmlDocument();
document.Load(@"C:\Documents and Settings\Kenny\My Documents\project\document.html");

var table = document.GetElementbyId("table5");
var tableRows = table.ChildNodes
                    .Where(cn => cn.NodeType == HtmlNodeType.Element)
                    .Skip(2);

Leider scheint es kein aktives Projekt zu sein. Die letzte Änderung laut CodePlex-Projektseite ist Juli 2012. Es ist möglich, dass es nicht viel Raum für Verbesserungen gibt, und basierend auf meiner vorläufigen Verwendung scheint es stabil und schnell zu sein.

Fizzler

Fizzler baut auf dem Html Agility Pack auf und bietet Unterstützung für die Verwendung von CSS-Selektoren für den Zugriff auf geparste HTML-Dokumente.

Leider scheint es, wie das Html Agility Pack, auch inaktiv zu sein; Die letzte Änderung, gemäß der Google Code-Quellliste, war die letzte Änderung im Januar 2013. Es ist auch möglich, dass sie ebenfalls stabil ist und keiner laufenden Entwicklung oder Wartung bedarf.

Andere Ressourcen

HAP ist immer noch beliebt, aber es ist fehlerhafter, schrulliger Müll und sollte in modernem Code vermieden werden. Der Fehler des falschen Parsens von HTML-Tags mit optionalen schließenden Tags ist nach vielen, vielen Jahren immer noch da. Es gibt immer noch Nullen anstelle von leeren Sammlungen zurück, wodurch der Code ausführlich wird. Auch HAP ist Ms-PL und Fizzler ist LGPL, nicht MIT/BSD, was in manchen Fällen wichtig sein kann. Verwenden Sie es einfach nicht, es gibt bessere Möglichkeiten.

CsQuery ist auch ein sehr guter HTML-Parser mit CSS-Selektoren. Es generiert das gleiche DOM wie Gecko-basierte Browser. Es hat auch eine viel bessere Lizenz (MIT) als das Html Agility Pack (MS-PL), das mit GPL nicht kompatibel ist.

Diese Bibliothek ist auch sehr einfach zu verwenden, da sie über eine jQuery-ähnliche API verfügt.

BEARBEITEN: Derzeit (25. Juni 2016) wird es nicht aktiv gepflegt. Es gibt also eine bessere Alternative wie AngleSharp.

CefSharp

Wieso den?

  • Aktiv gepflegt
  • Sie erhalten die Macht von Chromium
  • Lassen Sie uns ein beliebiges JavaScript ausführen. Es ist viel einfacher, das Parsen auf diese Weise zu entwickeln. Sie gehen zu Ihrer Chromium-basierten Browserkonsole und entwickeln das gewünschte Skript. Wenn Sie eine kleine C#-Codebasis geschrieben haben, besteht Ihre Art der Entwicklung darin, Javascript-Code aus der Konsole einzufügen, ohne C#-Schleifen und -Abfragen schreiben zu müssen.
  • Lassen Sie uns ein C#-Ereignis aus Ihrem JavaScript-Code auslösen. Es ist äußerst nützlich, wenn Sie ein AJAX-Erfolgsereignis auslösen möchten, um Ergebnisse zu erhalten.

Es gibt drei Arten von CefSharp:

  • CefSharp.WinForms
  • CefSharp.Wpf
  • CefSharp.OffScreen

Die ersten beiden werden wie IE-basierte WebBrowser in Windows.Forms verwendet. Aber es basiert auf Chromium. Und zum Parsen sollten Sie CefSharp.OffScreen verwenden.

Installieren Sie es über Nuget und verwenden Sie es.
Install-Package CefSharp.OffScreen -Version 57.0.0


Code

Die bereitgestellten Beispiele sind nicht so kurz wie möglich, aber sie erleichtern die Programmierung mit CefSharp.

Ich werde jQuery für Javascript-Aufrufe zur Demonstration und Beispielvereinfachung verwenden, vorausgesetzt, dass die Zielseite über diese Bibliothek verfügt. Sie können einfaches JS ausführen oder eines auswählen, das auf der Zielseite verfügbar ist.

Zunächst werden Javascript-Ergebnisse über die Eigenschaft JavascriptResponsetype zurückgegeben. Javascript-Arrays werden abgebildet auf . Andere Ergebnistypzuordnungen sind offensichtlich: , , aber sie werden alle in der Eigenschaft gespeichert . Um Javascript-Methoden generisch zu machen, verwende ich Folgendes .ResultobjectList<object>stringintboolobject ResultConvertHelper

public static class ConvertHelper
{
    public static T[] GetArrayFromObjectList<T>(object obj)
    {
        return ((IEnumerable<object>)obj)
            .Cast<T>()
            .ToArray();
    }

    public static List<T> GetListFromObjectList<T>(object obj)
    {
        return ((IEnumerable<object>)obj)
            .Cast<T>()
            .ToList();
    }

    public static T ToTypedVariable<T>(object obj)
    {
        if (obj == null)
        {
            dynamic dynamicResult = null;
            return dynamicResult;
        }

        Type type = typeof(T);
        if (type.IsArray)
        {
            dynamic dynamicResult = typeof(ConvertHelper).GetMethod(nameof(GetArrayFromObjectList))
                .MakeGenericMethod(type.GetElementType())
                .Invoke(null, new[] { obj });
            return dynamicResult;
        }

        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
        {
            dynamic dynamicResult = typeof(ConvertHelper).GetMethod(nameof(GetListFromObjectList))
                .MakeGenericMethod(type.GetGenericArguments().Single())
                .Invoke(null, new[] { obj });
            return dynamicResult;
        }

        return (T)obj;
    }
}

Ich habe eine Klasse hinzugefügt, um Javascript-Fehler zu behandeln:

public class JavascriptException : Exception
{
    public JavascriptException(string message): base(message) { }
}

Dann müssen wir unsere Kernklasse erstellen CefSharpWrapper, um die ganze Drecksarbeit mit Browserkram zu erledigen.

public class CefSharpWrapper
{
    private ChromiumWebBrowser _browser;

    public void InitializeBrowser()
    {
        CefSettings settings = new CefSettings();

        // Perform dependency check to make sure all relevant resources are in our output directory.
        Cef.Initialize(new CefSettings(), performDependencyCheck: true, browserProcessHandler: null);

        _browser = new ChromiumWebBrowser();

        // wait till browser initialised
        AutoResetEvent waitHandle = new AutoResetEvent(false);

        EventHandler onBrowserInitialized = null;

        onBrowserInitialized = (sender, e) =>
        {
            _browser.BrowserInitialized -= onBrowserInitialized;

            waitHandle.Set();
        };

        _browser.BrowserInitialized += onBrowserInitialized;

        waitHandle.WaitOne();
    }

    public void ShutdownBrowser()
    {
        // Clean up Chromium objects
        Cef.Shutdown();
    }

    public Task<T> GetResultAfterPageLoad<T>(string pageUrl, Func<Task<T>> onLoadCallback)
    {
        TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();

        EventHandler<LoadingStateChangedEventArgs> onPageLoaded = null;

        T t = default(T);

        // An event that is fired when the first page is finished loading.
        // This returns to us from another thread.
        onPageLoaded = async (sender, e) =>
        {
            // Check to see if loading is complete - this event is called twice, one when loading starts
            // second time when it's finished
            // (rather than an iframe within the main frame).
            if (!e.IsLoading)
            {
                // Remove the load event handler, because we only want one snapshot of the initial page.
                _browser.LoadingStateChanged -= onPageLoaded;

                t = await onLoadCallback();

                tcs.SetResult(t);
            }
        };

        _browser.LoadingStateChanged += onPageLoaded;

        _browser.Load(pageUrl);

        return tcs.Task;
    }

    // Method to get result via Javascript    
    public async Task<T> EvaluateJavascript<T>(string script)
    {
        JavascriptResponse javascriptResponse = await browser.GetMainFrame().EvaluateScriptAsync(script);

        if (javascriptResponse.Success)
        {
            object scriptResult = javascriptResponse.Result;
            return ConvertHelper.ToTypedVariable<T>(scriptResult);
        }

        throw new JavascriptException(javascriptResponse.Message);
    }
}

Dann rufen wir unsere CefSharpWrapperKlasse from- MainMethode auf, um alle a hrefvon der Stackoverflow-Homepage zu erhalten.

public class Program
{
    private static void Main()
    {
        MainAsync().Wait();
    }

    private static async Task MainAsync()
    {
        CefSharpWrapper wrapper = new CefSharpWrapper();

        wrapper.InitializeBrowser();

        string[] urls = await wrapper.GetResultAfterPageLoad("http://stackoverflow.com/", async () =>
            await wrapper.EvaluateJavascript<string[]>("$('a[href]').map((index, element) => $(element).prop('href')).toArray()"));

        wrapper.ShutdownBrowser();
    }
}

Hinweis: Diese Bibliothek unterscheidet kein leeres Array nullund undefined. Sie werden alle als zurückgegeben null. Um dies zu vermeiden NullReferenceException, fügen Sie entweder entsprechenden Code hinzu CefSharpWrapper(aber dann müssten Sie sich mit der Unterscheidung beschäftigen, ist nullin C# gemeint nulloder leeres Array in Javascript) oder fügen Sie folgenden Code hinzu Main.

if (urls == null) urls = new string[0];
Diese Frage bezieht sich nicht auf das Rendern von HTML. Ich konnte in den Projektinformationen auf GitHub nichts zum Parsen von HTML finden.
Ich weiss. Sie können diese Bibliothek zum Parsen verwenden und sie ist sehr komfortabel zum Parsen, deshalb empfehle ich sie. Sie rufen einfach die ChromiumWebBrowserMethode EvaluateScriptAsyncvon auf und mit Javascript erhalten Sie die gewünschten Ergebnisse. Wenn beispielsweise die Zielseite jQuery unterstützt und Sie alle href von a benötigen, können Sie _browser.EvaluateScriptAsync("$('a[href]').map((index, element) => $(element).prop('href')).toArray()"). Ich kann Ihnen Codebeispiele zeigen, bin mir aber nicht sicher, ob sie für diese Website relevant sind. Ich habe die aktuelle Bibliothek in der Produktion zum Analysieren von Websites verwendet.
Codebeispiele sind absolut relevant; Bitte fügen Sie ein einfaches Beispiel hinzu, wenn Sie können. Wie würde C#-Code beispielsweise Daten abrufen, die vom JavaScript-Code analysiert wurden? Dies ist eine sehr interessante Antwort auf die Frage. Ich ging implizit davon aus, dass der HTML-Code, den ich analysieren möchte, bereits bereitgestellt wird, aber die Verwendung von Chromium (oder einer beliebigen Browser-Engine) würde es ermöglichen, dass auch Dinge wie Single-Page-Web-Apps verarbeitet werden.
Sehr interessante (und sehr hackige ) Antwort!

Wenn Sie etwas wirklich Schnelles wollen, schauen Sie hier rein: Majestic-12 : Projects : C# HTML parser (.NET)

Es wird nicht das einfachste zu verwenden sein, aber es wird wahrscheinlich das schnellste sein.