HTMLAgilityPack中的XPath select不能按预期工作

时间:2014-04-23 00:25:16

标签: c# xpath screen-scraping

我在C#中编写简单的屏幕抓取程序,为此我需要选择所有输入放在一个名为" aspnetForm"的单个表单中(有两个表单)页面,我不想要来自另一个的输入),并且此表单中的所有输入都放在不同的表格中,或者只是在此表单的第一个子级别。

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

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

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

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

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

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

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

HTMLAgilityPack中的XPath目前是否已损坏? 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);
        }

    }
}

1 个答案:

答案 0 :(得分:4)

您的测试中出现意外行为,因为html包含<form>元素。以下是相关讨论:

  

Ariman:&#34;我发现在解析任何节点后都没有任何子节点。应该在表单(,等)中的所有节点都是作为兄弟姐妹而不是孩子创建的。

     

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

     

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

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