首页 > 解决方案 > Essential Objects WebView 如何在 HTML-tree 中导航?

问题描述

我正在使用基本对象库来读取网站。

我之前使用 windows 表单 webbrowser 完成了此操作,但这次该网站无法使用 windows forms webbrowser,因此我不得不更改为 EO webView。

纪录片太差了,我找不到答案。

在windows forms webbrowser中,你有一个HtmlElementCollection,它原则上是一个HtmlElement列表。在这些元素上,您可以读出属性或创建 InvokeMember("Click") 并浏览子/父元素。

EO WebView 中这个 HtmlElementCollection / HtmlElement 的等价物是什么?如何浏览 HTML 树?

顺便说一句:我将它与 C# 一起使用。

标签: c#html

解决方案


请参阅文档:hereherehere

本质上,您必须依赖执行 JavaScript 的能力。

您可以通过以下几种方式访问​​文档 JavaScript 对象:

JSObject document = (JSObject)_webView.EvalScript("document");

//or: Document document = _webView.GetDOMWindow().document;   

GetDOMWindow()返回一个EO.WebBrowser.DOM.Document实例;该类型派生自JSObject并提供了一些额外的属性(例如,有一个body属性可以让您获得 type 的 BODY 元素EO.WebBrowser.DOM.Element)。
但总的来说,这些提供的 API 并没有丰富多少。

您可以像这样使用 JSObject:

// access a property on the JavaScript object:
jsObj["children"]    

// access an element of an array-like JavaScript object:
var children = (JSObject)jsObj["children"];
var first = (JSObject)children[0];

// (note that you have to cast; all these have the `object` return type)

// access an attribute on the associated DOM element
jsObj.InvokeFunction("getAttribute", "class")

// etc.

这有点繁琐,但是您可以编写一些扩展方法来让您的生活更轻松(但是,请参阅下面的性能说明):

public static class JSObjectExtensions
{
    public static string GetTagName(this JSObject jsObj)
    {
        return (jsObj["tagName"] as string ?? string.Empty).ToUpper();
    }

    public static string GetID(this JSObject jsObj)
    {
        return jsObj["id"] as string ?? string.Empty;
    }

    public static string GetAttribute(this JSObject jsObj, string attribute)
    {
        return jsObj.InvokeFunction("getAttribute", attribute) as string ?? string.Empty;
    }

    public static JSObject GetParent(this JSObject jsObj)
    {
        return jsObj["parentElement"] as JSObject;
    }

    public static IEnumerable<JSObject> GetChildren(this JSObject jsObj)
    {
        var childrenCollection = (JSObject)jsObj["children"];
        int childObjectCount = (int)childrenCollection["length"];
        for (int i = 0; i < childObjectCount; i++)
        {
            yield return (JSObject)childrenCollection[i];
        }
    }

    // Add a few more if necessary
}

然后你可以做这样的事情:

private void TraverseElementTree(JSObject root, Action<JSObject> action)
{
    action(root);
    foreach(var child in root.GetChildren())
        TraverseElementTree(child, action);
}

以下是如何使用此方法的示例:

TraverseElementTree(document, (currentElement) =>
{
    string tagName = currentElement.GetTagName();
    string id = currentElement.GetID();
    if (tagName == "TD" && id.StartsWith("codetab"))
    {
        string elementClass = currentElement.GetAttribute("class");
        // do something...
    } 
});

但是,再一次,这有点繁琐——虽然这似乎工作得相当好,但您需要进行一些试验以找到任何可能导致错误的棘手部分,并弄清楚如何修改该方法以使其更稳定。

性能说明

另一种选择是使用 JavaScript 进行大部分元素处理,只返回需要在 C# 代码中使用的值。根据逻辑的复杂程度,在某些情况下这可能会更有效,因为它会导致单个浏览器引擎往返,因此如果性能成为问题,则需要考虑。(请参阅此处的性能部分。)


推荐阅读