【问题标题】:XPath select's in HTMLAgilityPack don't work as expectedHTMLAgilityPack 中的 XPath 选择不能按预期工作
【发布时间】:2014-06-07 14:23:50
【问题描述】:

我正在用 C# 编写简单的屏幕抓取程序,为此我需要选择放置在一个名为“aspnetForm”的单个表单中的所有输入(页面上有 2 个表单,我不希望输入来自另一个),并且此表单中的所有输入都放置在不同的表、div 中,或者只是在此表单的第一个子级。

所以我写了非常简单的 XPath 查询:

//form[@id='aspnetForm']//input

它在我测试的所有浏览器(Chrome、IE、Firefox)中都按预期工作 - 它返回我想要的。

但在 HTMLAgilityPack 中它根本不起作用 - SelectNodes 总是返回 NULL。

我为测试编写的这个查询工作正常,但返回的不是我想要的。首先为我的表单选择所有作为第一个孩子的输入,然后只选择返回的表单:

//form[@id='aspnetForm']/input
//form[@id='aspnetForm']

是的,我知道我可以枚举上一个查询中的节点,或者在它的结果上创建另一个 SelectNodes,但我真的不想这样做。我想使用与浏览器中相同的查询。

XPath 目前在 HTMLAgilityPack 中是否被破坏? C# 有任何替代的 XPath 实现吗?

更新:测试代码:

using HtmlAgilityPack;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace HtmlAGPTests
{
    [TestClass]
    public class XPathTests
    {
        private const string html =
                "<form id=\"aspnetForm\">" +
                "<input name=\"first\" value=\"first\" />" +
                "<div>" +
                    "<input name=\"second\" value=\"second\" />" +
                "</div>" +
                "</form>";

        private static HtmlNode GetHtmlDocumentNode()
        {
            var document = new HtmlDocument();
            document.LoadHtml(html);
            return document.DocumentNode;
        }

        [TestMethod]
        public void TwoLevelXpathTest()     // fail - nodes is NULL actually.
        {
            var query = "//form[@id='aspnetForm']//input";  // what i want
            var documentNode = GetHtmlDocumentNode();

            var inputNodes = documentNode.SelectNodes(query);

            Assert.IsTrue(inputNodes.Count == 2);
        }

        [TestMethod]
        public void TwoSingleLevelXpathsTest()     // works
        {
            var formQuery = "//form[@id='aspnetForm']";
            var inputQuery = "//input";
            var documentNode = GetHtmlDocumentNode();

            var formNode = documentNode.SelectSingleNode(formQuery);
            var inputNodes = formNode.SelectNodes(inputQuery);

            Assert.IsTrue(inputNodes.Count == 2);
        }

        [TestMethod]
        public void SingleLevelXpathTest()     // works
        {
            var query = "//form[@id='aspnetForm']";
            var documentNode = GetHtmlDocumentNode();

            var formNode = documentNode.SelectSingleNode(query);

            Assert.IsNotNull(formNode);
        }

    }
}

【问题讨论】:

  • .NET 内置了 HtmlAgilityPack 使用的 XPath 实现(HAP 不实现自己的 XPath 引擎)。实际上,HAP 的 XPath 过去对我来说工作得很好,所以我建议先怀疑其他的东西。
  • 尝试保存HtmlDocument,然后检查保存的文件是否包含预期的HTML格式。
  • @har07,它包含 - 我在发布问题之前对其进行了测试。 “肮脏的解决方法”方法也有效,因此输入绝对不是问题。向问题添加了测试代码,因此您可以自己测试它 - 它没有按预期工作。

标签: c# xpath screen-scraping


【解决方案1】:

测试中出现意外行为是因为 html 包含 &lt;form&gt; 元素。以下是相关讨论:

Ariman : “我发现在解析后任何节点都没有任何子节点。所有应该在表单内的节点 (, 等) 都被创建为它的兄弟姐妹而不是孩子们。

VikciaR : "在 Html 规范中表单标签可以重叠,所以 Htmlagilitypack 处理这个节点有点不同..."

[CodePlex discussion : No child nodes for FORM objects ]

按照 VikciaR 的建议,尝试像这样修改您的测试代码初始化:

private static HtmlNode GetHtmlDocumentNode()
{
    var document = new HtmlDocument();
    document.LoadHtml(html);
    
    //execute this line once
    HtmlNode.ElementsFlags.Remove("form");
    
    return document.DocumentNode;
}

旁注: 测试方法TwoSingleLevelXpathsTest() 中的inputQuery 值应为.//input。注意开头的点 (.) 表示此查询是相对于当前节点的。否则它将从根开始搜索,忽略前一个formQuery(没有点,您可以将formQuery 更改为任何内容,只要它不返回null,inputQuery 将始终返回相同的结果)。

【讨论】:

  • 默认情况下有点奇怪的行为..但无论如何,谢谢!它有效!
猜你喜欢
  • 2016-03-06
  • 2019-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-26
  • 2013-05-28
相关资源
最近更新 更多