有没有办法使用 C# 中的 Windows 窗体应用程序获取突出显示文本的 Xpath 位置

本文关键字:获取 显示 文本 位置 Xpath 应用程序 窗体 Windows 中的 有没有 | 更新日期: 2023-09-27 18:30:47

我正在尝试使用Windows表单应用程序创建一个网络抓取工具。是否有 API 或方法来显示所选文本的确切 xpath 位置?到目前为止,我已经编写了代码,允许我在 Web 浏览器控件中突出显示导航网站上的文本,并使用 ContextMenuStrip 将此突出显示的文本输出到富文本框中。

我在下面编写的代码是:


 private void getSelectedTextToolStripMenuItem_Click(object sender, EventArgs e)
    {
        IHTMLDocument2 htmlDocument = webBrowser1.Document.DomDocument as IHTMLDocument2;
        IHTMLSelectionObject currentSelection = htmlDocument.selection;
        if (currentSelection != null)
        {
            IHTMLTxtRange range = currentSelection.createRange() as IHTMLTxtRange;
            if (range != null)
            {
                richTextBox1.Text = range.htmlText;
            }

该按钮将导航到以下网站:

private void button1_Click(object sender, EventArgs e)
    {
        this.webBrowser1.Navigate("https://uk.finance.yahoo.com/q?s=%5EFTSE");
        webBrowser1.DocumentCompleted +=
        webBrowser1_DocumentCompleted;          
    }
到目前为止,它

完全符合我想要它做的事情。但是,我现在希望获得突出显示的任何内容的 xpath 位置,而不仅仅是输出文本内容。这个想法是,如果我想提取实时数据(即雅虎财经网页上的市场数据),网站上的数据会不断变化,所以我有兴趣在 html 页面结构中获得位置。关于这是否可能以及我应该遵循哪些步骤的任何想法?

有没有办法使用 C# 中的 Windows 窗体应用程序获取突出显示文本的 Xpath 位置

这是可能的,但您必须自己构建 XPath,方法是从所选元素向上移动层次结构,执行以下操作:

private void getSelectedXPathToolStripMenuItem_Click(object sender, EventArgs e)
{
    var doc = (IHTMLDocument2)webBrowser1.Document.DomDocument;
    IHTMLElement selectedElement = null;
    var sel = doc.selection;
    if (sel.type == "Text")
        selectedElement = ((IHTMLTxtRange)sel.createRange()).parentElement();
    else if (sel.type == "Control")
        selectedElement = ((IHTMLControlRange)sel.createRange()).commonParentElement();
    var node = (IHTMLDOMNode)selectedElement;
    MessageBox.Show(GetXPath(node, true));
}
string GetXPath(IHTMLDOMNode node, bool stopAtId)
{
    var path = new Stack<string>();
    while (node != null && node as IHTMLDocument2 == null)
    {
        var index = 0;
        // find previous siblings with the same tag name
        var prev = node.previousSibling;
        while (prev != null)
        {
            if (prev.nodeType == 1 && prev.nodeName == node.nodeName)
                index++;
            prev = prev.previousSibling;
        }
        var showIndex = index > 0;
        // if there were none, find if there are any next siblings with the same tag name
        var next = node.nextSibling;
        while (next != null)
        {
            if (next.nodeType == 1 && next.nodeName == node.nodeName)
            {
                showIndex = true;
                break;
            }
            next = next.nextSibling;
        }
        var id = ((IHTMLDOMAttribute2)((IHTMLAttributeCollection2)node.attributes).getNamedItem("id")).value;
        if (id != string.Empty)
        {
            showIndex = false;
        }
        var part = node.nodeName + (showIndex ? string.Format("[{0}]", index + 1) : string.Empty) + (id != string.Empty ? string.Format("[@id = '{0}']", id) : string.Empty);
        if (id != string.Empty && stopAtId)
            part = "/" + part;
        path.Push(part);
        if (id != string.Empty && stopAtId)
            break;
        node = node.parentNode;
    }
    return "/" + string.Join("/", path);
}

在此示例中,我创建了一个名为 getSelectedXPathToolStripMenuItem_Click 的新方法,该方法对应于上下文菜单上的新菜单项,以在消息框中的选择处显示 XPath。 显然,如果需要,您可以将其更改为将其放入 RTB 中。

主要工作是在 GetXPath 方法中完成的,该方法执行 DOM 遍历。它检查以前的同级节点,以确定节点的索引,如果它是具有该名称的第一个同级节点,则也会检查下一个同级,以查看是否应包含索引 (1)。

它还接受一个名为 stopAtId 的布尔参数,顾名思义,当节点设置了 id 属性时,它将停止遍历 DOM。 这可能很有用,因为您始终可以通过 id 轻松找到元素,而无需了解它的祖先等。