该库至少应支持.NET Framework 3.5和4.0版本;否则,请参见参考说明。对版本4.5和将来版本的支持也将非常好。

如果可能的话,它也应该处理'messy'HTML。

#1 楼

AngleSharp:


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


#2 楼

Html Agility Pack

我使用了Html Agility Pack,尽管其主页仅明确提到了2.0版,但它与.NET框架的4.0版非常兼容。我怀疑它在4.5版中也能正常工作。

以下是使用带有LINQ的Html Agility Pack的示例代码:似乎不是一个活跃的项目。根据项目CodePlex页面的最新更改是2012年7月。可能没有太多的改进空间,根据我的初步使用情况,它似乎稳定且快速。 > Fizzler建立在Html Agility Pack的基础上,并支持使用CSS选择器来访问已解析的HTML文档。根据Google代码源清单的最新更改,最近一次更改是2013年1月。它也可能很稳定,不需要持续的开发或维护。

其他资源


.net-在C#中解析html的最佳方法是什么? -堆栈溢出
用c#.net解析HTML-堆栈溢出


评论


HAP仍然很流行,但是它是多虫的古怪垃圾,在现代代码中应避免使用。许多年后,仍然存在对带有可选的结束标记的HTML标记进行不正确解析的错误。它仍然返回null而不是空集合,从而使代码变得冗长。 HAP也是Ms-PL,Fizzler是LGPL,而不是MIT / BSD,这在某些情况下可能很重要。只是不要使用它,所以有更好的选择。

– Athari
17年6月11日在5:08



#3 楼

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

该库也非常易于使用,因为它具有类似API的jQuery。

编辑:当前(2016年6月25日),它没有得到积极维护。因此,还有更好的替代品,例如AngleSharp。

#4 楼

CefSharp

为什么?


主动维护
您拥有Chromium的强大功能
让我们运行任何JavaScript。用这种方式开发解析要容易得多。您可以转到基于Chromium的浏览器控制台并开发所需的脚本。当您编写了一些C#代码基础后,您的开发方式就是从控制台粘贴Javascript代码,而无需编写C#循环和查询。
让您从JavaScript代码中触发C#事件。当您要触发AJAX成功事件以获取结果时,此功能非常有用。

CefSharp共有三种:


CefSharp.WinForms
CefSharp.Wpf
CefSharp.OffScreen

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

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


代码

提供的示例虽然不太简短,但它们会使使用CefSharp的编程更加容易。

我将使用jQuery假定目标站点具有此库,则Javascript要求进行演示和示例简化。您可以执行普通JS或选择目标站点上可用的任何JavaScript。

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


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

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


然后我们需要创建我们的核心CefSharpWrapper类,以使用浏览器的内容执行所有肮脏的工作。从stackoverflow主页获取所有CefSharpWrapper Main

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


注意:该库不能区分空数组ahref。它们都以null的形式返回。因此,要避免使用undefined,请在null中添加相应的代码(但您必须处理的区别是C#中的NullReferenceException表示Java中的CefSharpWrapper或Javascript中的空数组),或者在null中添加以下代码。 />

评论


这个问题与呈现HTML无关。我在GitHub的项目信息中找不到与解析HTML有关的任何内容。

–肯尼·埃维特(Kenny Evitt)
16-11-28在15:09

我知道。您可以使用该库进行解析,并且解析起来非常舒适,这就是我推荐它的原因。您只需调用ChromiumWebBrowser的EvaluateScriptAsync方法,并使用Javascript获得所需的结果。例如,如果目标站点支持jQuery,并且您需要所有href,则可以调用_browser.EvaluateScriptAsync(“ $('a [href]')。map((index,element)=> $(element).prop('href '))。toArray()“)。我可以向您展示代码示例,但不确定它是否与本网站相关。我已经在生产中使用当前库来解析网站。

– Vadim Ovchinnikov
16年11月28日在19:32

代码示例是绝对相关的;如果可以的话,请添加一个简单的示例。例如,C#代码将如何检索JavaScript代码解析的数据?这是一个非常有趣的答案。我隐式地假设已经提供了要解析的HTML,但是能够使用Chromium(或任何浏览器引擎)将允许处理单页Web应用程序之类的事情。

–肯尼·埃维特(Kenny Evitt)
16年11月28日在20:35

非常有趣(而且很hacky)的答案!

–肯尼·埃维特(Kenny Evitt)
16年12月19日在22:29

#5 楼

如果您想要真正快速的东西,请看这里:Majestic-12:项目:C#HTML解析器(.NET)

它不是最容易使用,但可能最快。