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.
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 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.
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.
Wieso den?
Es gibt drei Arten von CefSharp:
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
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 JavascriptResponse
type 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 .Result
object
List<object>
string
int
bool
object
Result
ConvertHelper
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 CefSharpWrapper
Klasse from- Main
Methode auf, um alle a
href
von 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 null
und 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 null
in C# gemeint null
oder leeres Array in Javascript) oder fügen Sie folgenden Code hinzu Main
.
if (urls == null) urls = new string[0];
ChromiumWebBrowser
Methode EvaluateScriptAsync
von 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.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.
Athari