用于解析 HTML 的 C# 库?

软件推荐 图书馆 html C# 。网
2021-10-26 00:16:58

该库应至少支持 .NET 框架版本 3.5 和 4.0;对 4.5 版和未来版本的支持也会很棒。

如果可能的话,它也应该处理“混乱”的 HTML。

4个回答

锐角

  • 积极开发/维护
  • 对 CSS 选择器的内置支持

Html 敏捷包

我使用了Html Agility Pack,虽然它的主页只明确提到了 2.0 版,但它与 .NET 框架的 4.0 版配合得很好。我怀疑它也适用于 4.5 版。

下面是一些使用带有 LINQ 的 Html Agility Pack 的示例代码:

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);

不幸的是,它似乎不是一个活跃的项目。根据项目 CodePlex 页面的最新更改是 2012 年 7 月。可能没有太大的改进空间,根据我的初步使用,它看起来稳定且快速。

菲兹勒

Fizzler构建在 Html Agility Pack 之上,并支持使用 CSS 选择器访问已解析的 HTML 文档。

不幸的是,与 Html Agility Pack 一样,它似乎也处于非活动状态;根据其 Google 代码源列表,最新的更改是 2013 年 1 月。也有可能它也很稳定,不需要持续的开发或维护。

其他资源

CsQuery也是非常好的带有 CSS 选择器的 HTML 解析器。它生成与基于 Gecko 的浏览器相同的 DOM。它还具有比 Html Agility Pack (MS-PL) 更好的许可证 (MIT),后者与 GPL 不兼容。

这个库也很容易使用,因为它有类似 jQuery 的 API。

编辑:目前(2016 年 6 月 25 日)它没有得到积极维护。所以有更好的选择,比如 AngleSharp。

头孢夏普

为什么?

  • 积极维护
  • 你得到了铬的力量
  • 让您运行任何 JavaScript。以这种方式开发解析要容易得多。你去你的基于 Chromium 的浏览器控制台并开发你想要的脚本。当您编写了一些小的 C# 代码基础时,您的开发方式只是从控制台粘贴 Javascript 代码,而无需编写 C# 循环和查询。
  • 让我们从 JavaScript 代码中触发 C# 事件。当您想要触发 AJAX 成功事件以获得结果时,它非常有用。

CefSharp 共有三种:

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

前两个与Windows.Forms 中基于 IE 的WebBrowser类似。但它是基于 Chromium 的。对于解析,您应该使用 CefSharp.OffScreen。

通过 Nuget 安装并使用它。
Install-Package CefSharp.OffScreen -Version 57.0.0


代码

提供的示例不是尽可能短,但它们将使使用 CefSharp 进行编程更容易。

假设目标站点具有此库,我将使用 jQuery 进行 Javascript 调用以进行演示和示例简单性。您可以执行纯 JS 或选择目标站点上可用的任何 JS。

首先,通过类型JavascriptResponse的属性返回 javascript 结果Javascript 数组映射到. 其他结果类型映射很明显:, 但它们都将存储在属性中。为了使 Javascript 方法通用,我使用以下.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;
    }
}

我添加了类来处理 Javascript 错误:

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

然后我们需要创建我们的核心CefSharpWrapper类来执行浏览器的所有脏活。

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);
    }
}

然后我们从方法中调用我们的CefSharpWrapper类以从 stackoverflow 主页Main获取 all 。a href

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();
    }
}

注意:这个库不区分空数组nullundefined. 它们都返回为null. 因此,为了避免NullReferenceException将相应的代码添加到CefSharpWrapper(但随后您必须处理区分nullC# 中的意思null或 Javascript 中的空数组)或将以下代码添加到Main.

if (urls == null) urls = new string[0];