在当今信息爆炸的时代,获取和分析竞争情报、创建热门搜索引擎或为应用注入有用的数据变得尤为重要。这里有一个工具可以让这项工作变得有趣:一个名为XHtmlKit的Nuget包。让开始吧:
首先,在Visual Studio中创建一个新项目。对于这个示例,将使用经典的桌面控制台应用程序。
接下来,在解决方案资源管理器中,右键单击“引用”,然后选择“管理Nuget包”:
然后,选择“浏览”标签页,输入“XHtmlKit”并按Enter键。从列表中选择XHtmlKit,然后点击“安装”。
现在,将在项目的引用中看到XHtmlKit。
首先,创建一个POCO类来保存爬虫结果。在这种情况下,将其命名为Article.cs:
namespace SampleScraper
{
public class Article
{
public string Category;
public string Title;
public string Rating;
public string Date;
public string Author;
public string Description;
public string Tags;
}
}
然后,创建一个类来执行爬虫操作。在这种情况下:
using System.Collections.Generic;
using System.Xml;
using XHtmlKit;
using System.Text;
using System.Threading.Tasks;
namespace SampleScraper
{
public static class MyScraper
{
public static async Task GetCodeProjectArticlesAsync(int pageNum = 1)
{
List results = new List();
string url = "https://www.codeproject.com/script/Articles/Latest.aspx?pgnum=" + pageNum;
XmlDocument page = await XHtmlLoader.LoadWebPageAsync(url);
var articles = page.SelectNodes("//table[contains(@class,'article-list')]/tr[@valign]");
foreach (XmlNode a in articles)
{
var category = a.SelectSingleNode("./td[1]//a/text()");
var title = a.SelectSingleNode(".//div[@class='title']/a/text()");
var date = a.SelectSingleNode(".//div[contains(@class,'modified')]/text()");
var rating = a.SelectSingleNode(".//div[contains(@class,'rating-stars')]/@title");
var desc = a.SelectSingleNode(".//div[@class='description']/text()");
var author = a.SelectSingleNode(".//div[contains(@class,'author')]/text()");
XmlNodeList tagNodes = a.SelectNodes(".//div[@class='t']/a/text()");
StringBuilder tags = new StringBuilder();
foreach (XmlNode tagNode in tagNodes)
tags.Append((tags.Length > 0 ? "," : "") + tagNode.Value);
Article article = new Article
{
Category = category != null ? category.Value : string.Empty,
Title = title != null ? title.Value : string.Empty,
Author = author != null ? author.Value : string.Empty,
Description = desc != null ? desc.Value : string.Empty,
Rating = rating != null ? rating.Value : string.Empty,
Date = date != null ? date.Value : string.Empty,
Tags = tags.ToString()
};
results.Add(article);
}
return results.ToArray();
}
}
}
然后,使用MyScraper类来获取一些数据:
using System;
namespace SampleScraper
{
class Program
{
static void Main(string[] args)
{
Article[] articles = MyScraper.GetCodeProjectArticlesAsync().Result;
foreach (Article a in articles)
{
Console.WriteLine(a.Date + ", " + a.Title + ", " + a.Rating);
}
}
}
}
现在,按F5键,观察结果的到来!
这个示例有几个关键元素。首先,这行代码:
XmlDocument page = await XHtmlLoader.LoadWebPageAsync(url);
是魔法发生的地方。在幕后,XHtmlKit使用HttpClient获取给定的网页,并解析原始流为XmlDocument。一旦加载到XmlDocument中,使用XPath获取想要的数据就变得简单了。注意LoadWebPageAsync()是一个异步方法,所以方法也将是async的!
锚定XPath语句获取所有具有valign属性的tr行,这些行直接位于具有包含术语'article-list'的class属性的table节点下:
var articles = page.SelectNodes("//table[contains(@class,'article-list')]/tr[@valign]");
这是一个相对健壮的XPath语句,因为术语'article-list'在语义上是清晰的。尽管网页的底层CSS格式可能会改变,但文章不太可能在没有重大页面重新设计的情况下移动到不同的标记位置。
最后,剩下的就是遍历文章节点,并从每个XHtml块中提取单个文章元素。在这里,使用带有'./'前缀的XPath语句。这告诉XPath评估器找到相对于当前上下文节点的节点,这非常高效。需要意识到给定的SelectSingleNode()语句可能返回数据,也可能不返回数据:
var category = a.SelectSingleNode("./td[1]//a/text()");
var title = a.SelectSingleNode(".//div[@class='title']/a/text()");
var date = a.SelectSingleNode(".//div[contains(@class,'modified')]/text()");
var rating = a.SelectSingleNode(".//div[contains(@class,'rating-stars')]/@title");
var desc = a.SelectSingleNode(".//div[@class='description']/text()");
var author = a.SelectSingleNode(".//div[contains(@class,'author')]/text()");
同样注意,个别字段选择使用尽可能语义化的XPath语句,如'title'、'rating-stars'和'author'!