所以您的问题是您依赖于 XPath 中的位置。虽然在某些情况下这可能没问题,但它不在这里,因为您期望给定的第 一个与班级有一个。td
tr
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 服务。这基本上是为了取回页面正在获取的相同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
返回的只是日历的物理 HTML。table
我们现在用它做什么?我们将它加载到 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 等工具。