我的问题是我无法从表中获取div InnerText。我已经成功地提取了不同类型的数据,但我不知道如何从表中读取div。
在下面的图片中我突出显示了div,我需要从中获取InnerText,在本例中为3号。
我正在尝试使用以下路径完成此操作:
"//div[@class='kal']//table//tr[2]/td[1]/div[@class='cipars']"
但是我得到了以下错误:
Click here for Error message picture
假设其余代码写得正确,有人能指出我正确的方向吗?我一直在努力想出这个,但我无法得到任何结果。
答案 0 :(得分:1)
所以你的问题是你依赖XPath中的位置。虽然在某些情况下这可能没问题,但它不在这里,因为您希望给定td
中的第一个 tr
与div
一起使用descendant
类。
查看Chrome中的来源,它表明并非总是这样。您可以通过将日历中的“1”元素与“2”和“3”进行比较来查看。您会注意到“1”元素周围有许多元素,而其他元素则没有。
您的原始XPath查询未返回元素,这就是您收到错误的原因。如果您给出HtmlAgilityPack的XPath查询不会产生DOM元素,它将返回null。
现在,因为您没有显示整个代码,所以我不知道这个代码是如何运行的。但是,我猜你正试图遍历所有的日历项目。无论如何,你有多种方法可以做到这一点,但我会告诉你,使用//div[@class='kal']//table//descendant::div[@class='cipars']
XPath选择器,可以一次性抓住所有这些:
tr
这将返回所有日历项目(即1到30)。
但是,要获取特定行中的所有项目,您只需将//div[@class='kal']//table//tr[3]/descendant::div[@class='cipars']
添加到查询中:
div
这将返回2到8(第二行日历项目)。
要定位特定的一个,那么你必须对网站的源代码做出假设。看起来每个“cipars”td
都有一个datums
的祖先和一个类//div[@class='kal']//table//tr[3]//td[@class='datums'][2]/div[@class='cipars']
....所以要从你的问题中得到“3”值:
table
希望这足以至少显示问题。
修改强>
虽然您确实遇到了XPath问题,但您还有其他问题。
网站创建非常奇怪。日历以奇怪的方式加载。当我点击该URL时,日历是由一些Javascript调用XML Web服务(用PHP编写)创建的,然后计算用于日历的完整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;
}
。
由于这是Javascript(客户端代码),HtmlAgilityPack将不会执行它。因此,HtmlAgilityPack甚至没有“看到”该表。因此,针对它的查询将返回“未找到”(null)。
解决方法:1)使用可调用脚本的工具。这个,我的意思是加载一个浏览器。一个很好的工具用于此Selenium。这可能是更好的整体解决方案,因为这意味着实际上将调用站点使用的所有脚本。您仍然可以使用XPath,因此您的查询不会更改。
第二种方法是将请求发送到该页面所用的相同的 Web服务。这基本上是为了获取页面获取的相同的 HTML,并使用 和HtmlAgilityPack。我们怎么做?
好吧,您可以使用C#轻松地将数据发布到Web服务。为了方便使用,我偷了this SO question的代码。有了这个,我们可以发送页面相同的请求,并返回相同的HTML。
因此,为了发送一些POST数据,我们生成一个类似的方法.....
string responseBody = SendPost("http://lekcijas.va.lv/lekcijas_request.php", "nodala=IT&kurss=1&gads=2013&menesis=9&c_dala=");
我们可以这样称呼它:
php
我是怎么得到的?那么我们调用的responseBody
文件是页面的Web服务,POST数据也是。我发现它发送给服务的数据的方式是调试Javascript(使用Chrome的开发者控制台),但你可能会注意到它与URL中的几乎相同。这似乎是故意的。
返回的table
是的物理HTML 日历的var document = new HtmlDocument();
document.LoadHtml(webpageContent);
。
我们现在用它做什么?我们将其加载到HtmlAgilityPack中,因为它能够接受纯HTML。
var node = document.DocumentNode.SelectSingleNode("//div[@class='kal']//table//tr[3]//td[@class='datums'][2]/div[@class='cipars']");
现在,我们坚持原来的XPath:
Console.WriteLine(node.InnerText);
现在,我们打印出有希望成为“3”的内容:
{{1}}
我的输出,在本地运行,确实是: 3 。
然而,虽然这可以解决你遇到的问题,但我假设网站的其余部分是这样的。如果是这种情况,你仍然可以使用上面的技术解决它,但是像Selenium这样的工具就是出于这个原因而创建的。