1

我的问题是我无法从表中获取 div InnerText。我已经成功提取了不同类型的数据,但我不知道如何从表中读取 div。

在下图中,我突出显示了 div,我需要从中获取 InnerText,在本例中为 3 号。

点击这里查看第一张图片

我正在尝试使用以下路径来完成此操作:

"//div[@class='kal']//table//tr[2]/td[1]/div[@class='cipars']"

但我收到以下错误:

单击此处查看错误消息图片

假设其余代码编写正确,有人能指出我正确的方向吗?我一直试图弄清楚这一点,但我无法得到任何结果。

4

1 回答 1

1

所以您的问题是您依赖于 XPath 中的位置。虽然在某些情况下这可能没问题,但它不在这里,因为您期望给定的 一个与班级有一个。tdtrdiv

查看 Chrome 中的源代码,它表明情况并非总是如此。您可以通过比较日历中的“1”元素与“2”和“3”来看到这一点。您会注意到“1”元素周围有许多元素,而其他元素则没有。

您的原始 XPath 查询不返回元素,这就是您收到错误的原因。如果您给 HtmlAgilityPack 的 XPath 查询没有产生 DOM 元素,它将返回 null。

现在,因为你没有展示你的整个代码,我不知道这段代码是如何运行的。但是,我猜您正在尝试遍历所有日历项目。无论如何,您有多种方法可以做到这一点,但我将向您展示使用descendantXPath 选择器,您可以一次性获取全部内容:

//div[@class='kal']//table//descendant::div[@class='cipars']

这将返回所有日历项(即 1 到 30)。

但是,要获取特定行中的所有项目,您可以将其粘贴tr到查询中:

//div[@class='kal']//table//tr[3]/descendant::div[@class='cipars']

这将返回 2 到 8(日历项的第二行)。

要针对特定​​的,你必须对网站的源代码做出假设。看起来每个“cipars”div都有一个td带有类的祖先datums......所以从你的问题中获得“3”值:

//div[@class='kal']//table//tr[3]//td[@class='datums'][2]/div[@class='cipars']

希望这至少足以说明问题。

编辑

尽管您确实遇到了 XPath 问题,但您也遇到了另一个问题。

该网站的创建非常奇怪。日历以一种奇怪的方式加载。当我点击该 URL 时,日历是由一些 Javascript 调用 XML Web 服务(用 PHP 编写)创建的,然后计算table用于日历的完整内容。

由于这是 Javascript(客户端代码),HtmlAgilityPack 不会执行它。因此,HtmlAgilityPack 甚至没有“看到”该表。因此,针对它的查询返回为“未找到”(null)。

解决方法:1)使用将调用脚本的工具。我的意思是加载浏览器。一个很好的工具叫做Selenium。这可能是更好的整体解决方案,因为这意味着站点使用的所有脚本都将被实际调用。您仍然可以将 XPath 与它一起使用,因此您的查询不会改变。

第二种方法是将请求发送到与页面相同的Web 服务。这基本上是为了取回页面正在获取的相同HTML,并将与 HtmlAgilityPack 一起使用。我们如何做到这一点?

好吧,您可以使用 C# 轻松地将数据发布到 Web 服务。只是为了便于使用,我从这个 SO question中窃取了代码。有了这个,我们可以发送与页面相同的请求,并返回相同的 HTML。

所以要发送一些 POST 数据,我们生成一个像这样的方法.....

public static string SendPost(string url, string postData)
{
    string webpageContent = string.Empty;

    byte[] byteArray = Encoding.UTF8.GetBytes(postData);

    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
    webRequest.Method = "POST";
    webRequest.ContentType = "application/x-www-form-urlencoded";
    webRequest.ContentLength = byteArray.Length;

    using (Stream webpageStream = webRequest.GetRequestStream())
    {
        webpageStream.Write(byteArray, 0, byteArray.Length);
    }

    using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
    {
        using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
        {
            webpageContent = reader.ReadToEnd();
        }
    }

    return webpageContent;
}

我们可以这样称呼它:

string responseBody = SendPost("http://lekcijas.va.lv/lekcijas_request.php", "nodala=IT&kurss=1&gads=2013&menesis=9&c_dala=");

我是怎么得到这个的?好吧,php我们调用的文件是页面所在的 Web 服务,POST 数据也是。我通过调试 Javascript(使用 Chrome 的开发者控制台)找出它发送给服务的数据的方法,您可能会注意到它与 URL 中的内容几乎相同。这似乎是故意的。

responseBody返回的只是日历的物理 HTMLtable

我们现在用它做什么?我们将它加载到 HtmlAgilityPack 中,因为它能够接受纯 HTML。

var document = new HtmlDocument();
document.LoadHtml(webpageContent);

现在,我们将原始 XPath 粘贴到:

var node = document.DocumentNode.SelectSingleNode("//div[@class='kal']//table//tr[3]//td[@class='datums'][2]/div[@class='cipars']");

现在,我们打印出应该是“3”的内容:

Console.WriteLine(node.InnerText);

我在本地运行的输出确实是:3

然而,虽然这会让你解决你遇到的问题,但我假设网站的其余部分都是这样的。如果是这种情况,您仍然可以使用上述技术解决它,但正是出于这个原因,创建了 Selenium 等工具。

于 2013-09-25T15:21:18.627 回答