某一个时刻,我们会有将页面中所有美图都下载到本地的冲动。本文提供一种C#的实现,但是只到提供图片URL的地步,毕竟有了URL,批量下载图片实在是太简单了。
解析HTML的DOM结构乍一看并不是多难的事情,获得源代码之后类似于XML的解析即可。可真正做过XML解析的人就会知道另一个事实:解析XML从来都不容易啊,各种兼容性都要考虑到,少了个引号,多了个右破折号之类的会让人瞬间抓狂。针对HTML还有更多的问题,你得兼容各种奇怪的写法,也许你还得解析上个世纪的一些老网页,那种你看到源代码就会撞墙的事情相信你也不想尝试。
所以,HtmlAgilityPack的问世,就会让你瞬间觉得这个时间还是很美好,很可爱的。
首先,毫无疑问用Nuget Package下载最新的release版本。
PM> Install-Package HtmlAgilityPack
接下来,我们来获取页面的源代码。需要注意的是HtmlAgilityPack是不支持直接从网址中获得源码并解析的,换言之你必须先把源码用自己的方式下载下来,然后以字符串的形式进行解析。不知道作者是出于什么考虑才这样设计的,但不得不得说这样灵活性很大。
static async Task<HttpResponseMessage> RetrievePage(string url)
{
var client = new HttpClient();
var result = await client.GetAsync(url);
return result;
}
上面使用了C# 5.0的一些异步语法,除了async
和await
这两个新面孔外与同步写法没有太大差别。当然上面是获得了HttpResponseMessage
对象而已,要获得源代码还得进一步解析。
static async Task<HtmlDocument> GetDocument(HttpResponseMessage responeMessage)
{
var content = await responeMessage.Content.ReadAsStringAsync();
if (!responeMessage.IsSuccessStatusCode)
{
throw new FileNotFoundException("Unable to get doucment.");
}
var doc = new HtmlDocument();
doc.LoadHtml(content);
return doc;
}
我们首先检查返回的状态码,如果不是200就抛出异常,当然在实际情况下肯定会用户友好的以另一种方式“抛出”。紧接着便是HtmlAligityPack加载Html源码的方式,这样最终我们会获得一个Task<HtmlDocument>
类型的对象。从此以后,就算是正式开始解析DOM结构了。
static void Main(string[] args)
{
var page = RetrievePage("http://www.microsoft.com/en-us/default.aspx");
var doc = GetDocument(page.Result);
var html = doc.Result;
var images = html.DocumentNode.SelectNodes("//img");
foreach (var img in images)
{
Console.WriteLine(img.Attributes["src"].Value);
}
}
利用SelectNodes
这个API,我们可以获得页面中所有img
标签的HtmlNode
对象,前面的“//”是XPATH的一种语法,如果它位于模式的最前面,比如代码中的“//img”,那么它便可匹配以根元素为相对位置的任意img节点。换言之,从源代码中以最省力的方式获得某类节点,直接以“//”开头便可。
接下来,遍历打印每个节点的src
属性。由此我们便得到了所有img
节点的图片地址,接下来只要使用我们最喜欢的方式将它们异步下载下来即可。
所以,总的来说HtmlAgilityPack已经帮我们把最恶心的工作做完了,从此以后我们看到页面中有任何美图都无所畏惧了。