Html Agility Pack - 在表中读取div InnerText

时间:2013-09-25 14:28:05

标签: c# web-scraping html-agility-pack

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

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

Click here for first picture

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

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

但是我得到了以下错误:

Click here for Error message picture

假设其余代码写得正确,有人能指出我正确的方向吗?我一直在努力想出这个,但我无法得到任何结果。

1 个答案:

答案 0 :(得分:1)

所以你的问题是你依赖XPath中的位置。虽然在某些情况下这可能没问题,但它不在这里,因为您希望给定td中的第一个 trdiv一起使用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这样的工具就是出于这个原因而创建的。