所以您的问题是您依赖于 XPath 中的位置。虽然在某些情况下可以这样做,但它不在这里,因为您期望给定 tr 中的 first td 与该类有 div。
查看 Chrome 中的源代码,发现情况并非总是如此。您可以通过比较日历中的“1”元素与“2”和“3”来看到这一点。您会注意到“1”元素周围有许多元素,而其他元素则没有。
您的原始 XPath 查询未返回元素,这就是您收到错误的原因。如果您提供给 HtmlAgilityPack 的 XPath 查询没有生成 DOM 元素,它将返回 null。
现在,由于您没有显示整个代码,我不知道这段代码是如何运行的。但是,我猜您正在尝试遍历所有日历项目。无论如何,您有多种方法可以做到这一点,但我将向您展示使用 descendant XPath 选择器,您可以一次性获取全部内容:
//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 服务发送请求。这基本上是为了取回页面正在获取的 same HTML,并将 that 与 HtmlAgilityPack 一起使用。我们如何做到这一点?
好吧,您可以使用 C# 轻松地将数据发布到 Web 服务。只是为了便于使用,我从this 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 是只是日历的table 的物理HTML。
我们现在用它做什么?我们将它加载到 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 等工具。